From 1a1e3345f4d77f1d0006ace29553e3975074f3f1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 19 Feb 2023 10:10:27 +0800 Subject: [PATCH 001/149] chore: reset tunName in macos when it isn't startWith "utun" --- listener/sing_tun/server.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 5c387a8d..8e0a6c34 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -67,6 +67,26 @@ func CalculateInterfaceName(name string) (tunName string) { return } +func checkTunName(tunName string) (ok bool) { + defer func() { + if !ok { + log.Warnln("[TUN] Unsupported tunName(%s) in %s, force regenerate by ourselves.", tunName, runtime.GOOS) + } + }() + if runtime.GOOS == "darwin" { + if len(tunName) <= 4 { + return false + } + if tunName[:4] == "utun" { + return false + } + if _, parseErr := strconv.ParseInt(tunName[4:], 10, 16); parseErr != nil { + return false + } + } + return true +} + func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) { if len(additions) == 0 { additions = []inbound.Addition{ @@ -75,7 +95,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte } } tunName := options.Device - if tunName == "" { + if tunName == "" || !checkTunName(tunName) { tunName = CalculateInterfaceName(InterfaceName) options.Device = tunName } From db3e1b9ed56756225d65265b8f0a253a82fe0a2c Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 19 Feb 2023 16:20:30 +0800 Subject: [PATCH 002/149] feat: add sni field for tuic --- adapter/outbound/tuic.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 0ca13670..5b7bde6e 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -51,6 +51,7 @@ type TuicOption struct { ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` ReceiveWindow int `proxy:"recv-window,omitempty"` DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` + SNI string `proxy:"sni,omitempty"` } // DialContext implements C.ProxyAdapter @@ -106,12 +107,14 @@ func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.Pack func NewTuic(option TuicOption) (*Tuic, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) serverName := option.Server - tlsConfig := &tls.Config{ ServerName: serverName, InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, } + if option.SNI != "" { + tlsConfig.ServerName = option.SNI + } var bs []byte var err error From baaf509637e7cef01eec8c56ad73dfb8852d1ba2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 Feb 2023 21:58:37 +0800 Subject: [PATCH 003/149] chore: using sing-shadowtls to support shadowtls v1/2/3 --- adapter/outbound/shadowsocks.go | 70 +++++++++++++-------- adapter/outbound/wireguard.go | 8 +-- component/tls/utls.go | 2 +- go.mod | 17 ++--- go.sum | 34 +++++----- listener/sing/log.go | 41 ------------ listener/sing_tun/server.go | 2 +- log/sing.go | 68 ++++++++++++++++++++ transport/sing-shadowtls/shadowtls.go | 91 +++++++++++++++++++++++++++ 9 files changed, 236 insertions(+), 97 deletions(-) delete mode 100644 listener/sing/log.go create mode 100644 log/sing.go create mode 100644 transport/sing-shadowtls/shadowtls.go diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 54566666..f6701469 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -10,10 +10,9 @@ import ( "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" - tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/transport/shadowtls" obfs "github.com/Dreamacro/clash/transport/simple-obfs" + shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls" "github.com/Dreamacro/clash/transport/socks5" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" @@ -33,7 +32,7 @@ type ShadowSocks struct { obfsMode string obfsOption *simpleObfsOption v2rayOption *v2rayObfs.Option - shadowTLSOption *shadowTLSOption + shadowTLSOption *shadowtls.ShadowTLSOption tlsConfig *tls.Config } @@ -67,14 +66,31 @@ type v2rayObfsOption struct { } type shadowTLSOption struct { - Password string `obfs:"password"` - Host string `obfs:"host"` - Fingerprint string `obfs:"fingerprint,omitempty"` - SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + Password string `obfs:"password"` + Host string `obfs:"host"` + Fingerprint string `obfs:"fingerprint,omitempty"` + ClientFingerprint string `obfs:"client-fingerprint,omitempty"` + SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + Version int `obfs:"version,omitempty"` } // StreamConn implements C.ProxyAdapter func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { + switch ss.obfsMode { + case shadowtls.Mode: + // fix tls handshake not timeout + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + var err error + c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption) + if err != nil { + return nil, err + } + } + return ss.streamConn(c, metadata) +} + +func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { switch ss.obfsMode { case "tls": c = obfs.NewTLSObfs(c, ss.obfsOption.Host) @@ -87,8 +103,6 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } - case shadowtls.Mode: - c = shadowtls.NewShadowTLS(c, ss.shadowTLSOption.Password, ss.tlsConfig) } if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) @@ -113,7 +127,15 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale safeConnClose(c, err) }(c) - c, err = ss.StreamConn(c, metadata) + switch ss.obfsMode { + case shadowtls.Mode: + c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption) + if err != nil { + return nil, err + } + } + + c, err = ss.streamConn(c, metadata) return NewConn(c, ss), err } @@ -171,7 +193,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { var v2rayOption *v2rayObfs.Option var obfsOption *simpleObfsOption - var shadowTLSOpt *shadowTLSOption + var shadowTLSOpt *shadowtls.ShadowTLSOption var tlsConfig *tls.Config obfsMode := "" @@ -210,24 +232,20 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } } else if option.Plugin == shadowtls.Mode { obfsMode = shadowtls.Mode - shadowTLSOpt = &shadowTLSOption{} - if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); err != nil { + opt := &shadowTLSOption{ + Version: 2, + } + if err := decoder.Decode(option.PluginOpts, opt); err != nil { return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err) } - tlsConfig = &tls.Config{ - NextProtos: shadowtls.DefaultALPN, - MinVersion: tls.VersionTLS12, - InsecureSkipVerify: shadowTLSOpt.SkipCertVerify, - ServerName: shadowTLSOpt.Host, - } - - if len(shadowTLSOpt.Fingerprint) == 0 { - tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) - } else { - if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil { - return nil, err - } + shadowTLSOpt = &shadowtls.ShadowTLSOption{ + Password: opt.Password, + Host: opt.Host, + Fingerprint: opt.Fingerprint, + ClientFingerprint: opt.ClientFingerprint, + SkipCertVerify: opt.SkipCertVerify, + Version: opt.Version, } } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index d1a5ea6e..e3dafbbf 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -17,7 +17,7 @@ import ( "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/listener/sing" + "github.com/Dreamacro/clash/log" wireguard "github.com/metacubex/sing-wireguard" @@ -174,14 +174,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{ Verbosef: func(format string, args ...interface{}) { - sing.Logger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) + log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) }, Errorf: func(format string, args ...interface{}) { - sing.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...)) + log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...)) }, }, option.Workers) if debug.Enabled { - sing.Logger.Trace("created wireguard ipc conf: \n", ipcConf) + log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf) } err = outbound.device.IpcSet(ipcConf) if err != nil { diff --git a/component/tls/utls.go b/component/tls/utls.go index f965fc64..4724c9a5 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -7,7 +7,7 @@ import ( "github.com/Dreamacro/clash/log" "github.com/mroth/weightedrand/v2" - utls "github.com/refraction-networking/utls" + utls "github.com/sagernet/utls" ) type UConn struct { diff --git a/go.mod b/go.mod index c2a979dc..b7d374ca 100644 --- a/go.mod +++ b/go.mod @@ -25,11 +25,12 @@ require ( 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/refraction-networking/utls v1.2.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9 + github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b + github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb 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 github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 @@ -38,11 +39,11 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.5.0 + golang.org/x/crypto v0.6.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/net v0.5.0 + golang.org/x/net v0.6.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.4.0 + golang.org/x/sys v0.5.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.1.7 @@ -50,7 +51,7 @@ require ( require ( github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.4 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect @@ -59,7 +60,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.15.12 // indirect + github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e // indirect @@ -75,7 +76,7 @@ require ( github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect golang.org/x/mod v0.6.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.2.0 // indirect ) diff --git a/go.sum b/go.sum index 076442ab..cacccf97 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -67,8 +67,8 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -121,8 +121,6 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4 github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI= github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo= -github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g= @@ -131,12 +129,16 @@ 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.7-0.20230207063819-27d2950cdbe9 h1:qnXh4RjHsNjdZXkfbqwVqAzYUfc160gfkS5gepmsA+A= -github.com/sagernet/sing v0.1.7-0.20230207063819-27d2950cdbe9/go.mod h1:JLSXsPTGRJFo/3X7EcAOCUgJH2/gAoxSJgBsnCZRp/w= +github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo= +github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/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.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM= github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY= 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= +github.com/sagernet/utls v0.0.0-20230220130002-c08891932056/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= @@ -169,8 +171,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -190,8 +192,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -220,14 +222,14 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/listener/sing/log.go b/listener/sing/log.go deleted file mode 100644 index 4847e063..00000000 --- a/listener/sing/log.go +++ /dev/null @@ -1,41 +0,0 @@ -package sing - -import ( - "fmt" - - "github.com/Dreamacro/clash/log" - - L "github.com/sagernet/sing/common/logger" -) - -type logger struct{} - -func (l logger) Trace(args ...any) { - log.Debugln(fmt.Sprint(args...)) -} - -func (l logger) Debug(args ...any) { - log.Debugln(fmt.Sprint(args...)) -} - -func (l logger) Info(args ...any) { - log.Infoln(fmt.Sprint(args...)) -} - -func (l logger) Warn(args ...any) { - log.Warnln(fmt.Sprint(args...)) -} - -func (l logger) Error(args ...any) { - log.Errorln(fmt.Sprint(args...)) -} - -func (l logger) Fatal(args ...any) { - log.Fatalln(fmt.Sprint(args...)) -} - -func (l logger) Panic(args ...any) { - log.Fatalln(fmt.Sprint(args...)) -} - -var Logger L.Logger = logger{} diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 8e0a6c34..63d475cf 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -237,7 +237,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: udpTimeout, Handler: handler, - Logger: sing.Logger, + Logger: log.SingLogger, }) if err != nil { return diff --git a/log/sing.go b/log/sing.go new file mode 100644 index 00000000..818acc79 --- /dev/null +++ b/log/sing.go @@ -0,0 +1,68 @@ +package log + +import ( + "context" + "fmt" + + L "github.com/sagernet/sing/common/logger" +) + +type singLogger struct{} + +func (l singLogger) TraceContext(ctx context.Context, args ...any) { + Debugln(fmt.Sprint(args...)) +} + +func (l singLogger) DebugContext(ctx context.Context, args ...any) { + Debugln(fmt.Sprint(args...)) +} + +func (l singLogger) InfoContext(ctx context.Context, args ...any) { + Infoln(fmt.Sprint(args...)) +} + +func (l singLogger) WarnContext(ctx context.Context, args ...any) { + Warnln(fmt.Sprint(args...)) +} + +func (l singLogger) ErrorContext(ctx context.Context, args ...any) { + Errorln(fmt.Sprint(args...)) +} + +func (l singLogger) FatalContext(ctx context.Context, args ...any) { + Fatalln(fmt.Sprint(args...)) +} + +func (l singLogger) PanicContext(ctx context.Context, args ...any) { + Fatalln(fmt.Sprint(args...)) +} + +func (l singLogger) Trace(args ...any) { + Debugln(fmt.Sprint(args...)) +} + +func (l singLogger) Debug(args ...any) { + Debugln(fmt.Sprint(args...)) +} + +func (l singLogger) Info(args ...any) { + Infoln(fmt.Sprint(args...)) +} + +func (l singLogger) Warn(args ...any) { + Warnln(fmt.Sprint(args...)) +} + +func (l singLogger) Error(args ...any) { + Errorln(fmt.Sprint(args...)) +} + +func (l singLogger) Fatal(args ...any) { + Fatalln(fmt.Sprint(args...)) +} + +func (l singLogger) Panic(args ...any) { + Fatalln(fmt.Sprint(args...)) +} + +var SingLogger L.ContextLogger = singLogger{} diff --git a/transport/sing-shadowtls/shadowtls.go b/transport/sing-shadowtls/shadowtls.go new file mode 100644 index 00000000..0e1e95c0 --- /dev/null +++ b/transport/sing-shadowtls/shadowtls.go @@ -0,0 +1,91 @@ +package sing_shadowtls + +import ( + "context" + "crypto/tls" + "net" + + tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/log" + + "github.com/sagernet/sing-shadowtls" + sing_common "github.com/sagernet/sing/common" + utls "github.com/sagernet/utls" +) + +const ( + Mode string = "shadow-tls" +) + +var ( + DefaultALPN = []string{"h2", "http/1.1"} +) + +type ShadowTLSOption struct { + Password string + Host string + Fingerprint string + ClientFingerprint string + SkipCertVerify bool + Version int +} + +func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) { + tlsConfig := &tls.Config{ + NextProtos: DefaultALPN, + MinVersion: tls.VersionTLS12, + InsecureSkipVerify: option.SkipCertVerify, + ServerName: option.Host, + } + + var err error + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalTLSConfig(tlsConfig) + } else { + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } + + tlsHandshake := shadowtls.DefaultTLSHandshakeFunc(option.Password, tlsConfig) + if len(option.ClientFingerprint) != 0 { + if fingerprint, exists := tlsC.GetFingerprint(option.ClientFingerprint); exists { + tlsHandshake = uTLSHandshakeFunc(tlsConfig, *fingerprint.ClientHelloID) + } + } + client, err := shadowtls.NewClient(shadowtls.ClientConfig{ + Version: option.Version, + Password: option.Password, + TLSHandshake: tlsHandshake, + Logger: log.SingLogger, + }) + if err != nil { + return nil, err + } + return client.DialContextConn(ctx, conn) +} + +func uTLSHandshakeFunc(config *tls.Config, clientHelloID utls.ClientHelloID) shadowtls.TLSHandshakeFunc { + return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error { + tlsConfig := &utls.Config{ + Rand: config.Rand, + Time: config.Time, + VerifyPeerCertificate: config.VerifyPeerCertificate, + RootCAs: config.RootCAs, + NextProtos: config.NextProtos, + ServerName: config.ServerName, + InsecureSkipVerify: config.InsecureSkipVerify, + CipherSuites: config.CipherSuites, + MinVersion: config.MinVersion, + MaxVersion: config.MaxVersion, + CurvePreferences: sing_common.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID { + return utls.CurveID(it) + }), + SessionTicketsDisabled: config.SessionTicketsDisabled, + Renegotiation: utls.RenegotiationSupport(config.Renegotiation), + SessionIDGenerator: sessionIDGenerator, + } + tlsConn := utls.UClient(conn, tlsConfig, clientHelloID) + return tlsConn.HandshakeContext(ctx) + } +} From 5c8d955f61c51933909b9d722ede84958234bb65 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 Feb 2023 13:41:33 +0800 Subject: [PATCH 004/149] chore: better windows bind error handle --- component/dialer/bind_windows.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/component/dialer/bind_windows.go b/component/dialer/bind_windows.go index b680e90f..4a099169 100644 --- a/component/dialer/bind_windows.go +++ b/component/dialer/bind_windows.go @@ -37,14 +37,25 @@ func bindControl(ifaceIdx int) controlFn { var innerErr error err = c.Control(func(fd uintptr) { handle := syscall.Handle(fd) + bind6err := bind6(handle, ifaceIdx) + bind4err := bind4(handle, ifaceIdx) switch network { - case "tcp6", "udp6": - innerErr = bind6(handle, ifaceIdx) - _ = bind4(handle, ifaceIdx) - default: - innerErr = bind4(handle, ifaceIdx) - // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 - _ = bind6(handle, ifaceIdx) + case "ip6", "tcp6": + innerErr = bind6err + case "ip4", "tcp4", "udp4": + innerErr = bind4err + case "udp6": + // golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "") + if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil { + // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 + if bind4err != nil { + innerErr = bind6err + } else { + innerErr = bind4err + } + } else { + innerErr = bind6err + } } }) From 4a6ebff473c93e1c4c43c1f2e2411408ff5b57e6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 Feb 2023 19:14:11 +0800 Subject: [PATCH 005/149] fix: add "dns resolve failed" error in dialer --- component/dialer/dialer.go | 127 +++++++++++++++---------------------- 1 file changed, 50 insertions(+), 77 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index e31936e9..39e47bde 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -65,18 +65,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option } func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) { - cfg := &option{ - interfaceName: DefaultInterface.Load(), - routingMark: int(DefaultRoutingMark.Load()), - } - - for _, o := range DefaultOptions { - o(cfg) - } - - for _, o := range options { - o(cfg) - } + cfg := applyOptions(options...) lc := &net.ListenConfig{} if cfg.interfaceName != "" { @@ -132,6 +121,35 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) } +func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + + var ip netip.Addr + switch network { + case "tcp4", "udp4": + if opt.resolver == nil { + ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) + } else { + ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver) + } + default: + if opt.resolver == nil { + ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) + } else { + ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver) + } + } + if err != nil { + err = fmt.Errorf("dns resolve failed:%w", err) + return nil, err + } + + return dialContext(ctx, network, ip, port, opt) +} + func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { @@ -178,6 +196,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt } } if result.error != nil { + result.error = fmt.Errorf("dns resolve failed:%w", result.error) return } result.resolved = true @@ -225,26 +244,6 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt return nil, err } -func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, err - } - - var ips []netip.Addr - if opt.resolver != nil { - ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver) - } else { - ips, err = resolver.LookupIPProxyServerHost(ctx, host) - } - - if err != nil { - return nil, err - } - - return concurrentDialContext(ctx, network, ips, port, opt) -} - func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { returned := make(chan struct{}) defer close(returned) @@ -356,77 +355,51 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr return nil, finalError } -func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, err - } - - var ip netip.Addr - switch network { - case "tcp4", "udp4": - if opt.resolver == nil { - ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) - } else { - ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver) - } - default: - if opt.resolver == nil { - ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) - } else { - ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver) - } - } - if err != nil { - return nil, err - } - - return dialContext(ctx, network, ip, port, opt) -} - func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { - switch network { - case "tcp4", "udp4": - return concurrentIPv4DialContext(ctx, network, address, opt) - default: - return concurrentIPv6DialContext(ctx, network, address, opt) - } -} - -func concurrentIPv4DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } var ips []netip.Addr - if opt.resolver == nil { - ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver) + switch network { + case "tcp4", "udp4": + if opt.resolver == nil { + ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) + } else { + ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver) + } + default: + if opt.resolver == nil { + ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) + } else { + ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver) + } } if err != nil { + err = fmt.Errorf("dns resolve failed:%w", err) return nil, err } return concurrentDialContext(ctx, network, ips, port, opt) } -func concurrentIPv6DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { +func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } var ips []netip.Addr - if opt.resolver == nil { - ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) + if opt.resolver != nil { + ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver) } else { - ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver) + ips, err = resolver.LookupIPProxyServerHost(ctx, host) } if err != nil { + err = fmt.Errorf("dns resolve failed:%w", err) return nil, err } From 28c57c41444063c1496859fd860427c973dff859 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 Feb 2023 19:35:43 +0800 Subject: [PATCH 006/149] chore: Update dependencies --- go.mod | 17 ++++++++--------- go.sum | 34 ++++++++++++++++------------------ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index b7d374ca..9c3b78ac 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.2 - github.com/gofrs/uuid v4.3.1+incompatible + github.com/gofrs/uuid v4.4.0+incompatible github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 @@ -20,7 +20,7 @@ require ( 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-tun v0.1.1-0.20230213124625-28d27a0c236b + 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 @@ -28,7 +28,7 @@ require ( github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e - github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb + github.com/sagernet/sing-vmess v0.1.2 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 @@ -44,7 +44,7 @@ require ( golang.org/x/net v0.6.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 - google.golang.org/protobuf v1.28.1 + google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.1.7 ) @@ -63,7 +63,7 @@ require ( github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e // indirect + github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -71,14 +71,13 @@ require ( github.com/quic-go/qtls-go1-18 v0.2.0 // indirect github.com/quic-go/qtls-go1-19 v0.2.0 // indirect github.com/quic-go/qtls-go1-20 v0.1.0 // indirect - github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect - golang.org/x/mod v0.6.0 // indirect + golang.org/x/mod v0.7.0 // indirect golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - golang.org/x/tools v0.2.0 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/tools v0.5.0 // indirect ) replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 diff --git a/go.sum b/go.sum index cacccf97..c06b8506 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -87,14 +87,14 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e h1:j4j2dlV2d//FAsQlRUriH6nvv36AEAhECbNy7narf1M= -github.com/metacubex/gvisor v0.0.0-20230213124051-7a16c835d80e/go.mod h1:abc7OdNmWlhcNHz84ECEosd5ND5pnWQmD8W55p/4cuc= +github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 h1:WUINdCB/UvSX9I+wN+y5xVEisPrXA73rxkoHK5DMyZs= +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-tun v0.1.1-0.20230213124625-28d27a0c236b h1:ZF/oNrSCaxIFoZmFQCiUx67t9aENZjyuqw2n4zw3L2o= -github.com/metacubex/sing-tun v0.1.1-0.20230213124625-28d27a0c236b/go.mod h1:TjuaYuR/g1MaY3um89xTfRNt61FJ2IcI/m5zD8QBxw4= +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= github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4/go.mod h1:p2VpJuxRefgVMxc8cmatMGSFNvYbjMYMsXJOe7qFstw= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= @@ -122,8 +122,6 @@ github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05 github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI= github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34= -github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= @@ -133,8 +131,8 @@ github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9Aito github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/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.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM= -github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY= +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/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= @@ -178,8 +176,8 @@ golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -230,23 +228,23 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= +golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ= +google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 21848d6bf1fb2707f3d9115bab9537e52c21354f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 Feb 2023 19:43:32 +0800 Subject: [PATCH 007/149] chore: code cleanup --- adapter/outbound/shadowsocks.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index f6701469..ae404eec 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -2,7 +2,6 @@ package outbound import ( "context" - "crypto/tls" "errors" "fmt" "net" @@ -33,7 +32,6 @@ type ShadowSocks struct { obfsOption *simpleObfsOption v2rayOption *v2rayObfs.Option shadowTLSOption *shadowtls.ShadowTLSOption - tlsConfig *tls.Config } type ShadowSocksOption struct { @@ -194,7 +192,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { var v2rayOption *v2rayObfs.Option var obfsOption *simpleObfsOption var shadowTLSOpt *shadowtls.ShadowTLSOption - var tlsConfig *tls.Config obfsMode := "" decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) @@ -266,7 +263,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { v2rayOption: v2rayOption, obfsOption: obfsOption, shadowTLSOption: shadowTLSOpt, - tlsConfig: tlsConfig, }, nil } From f586f22ce35c4df2126bb27385116f001ad3c53e Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 22 Feb 2023 21:08:08 +0800 Subject: [PATCH 008/149] fix: incorrect time to set interface name --- config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config/config.go b/config/config.go index d71fc63f..24159d8e 100644 --- a/config/config.go +++ b/config/config.go @@ -447,7 +447,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.General = general - dialer.DefaultInterface.Store(config.General.Interface) proxies, providers, err := parseProxies(rawCfg) if err != nil { return nil, err From 7fecd20a1d2dc921261e08351d115d125ef0348f Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 22 Feb 2023 22:32:04 +0800 Subject: [PATCH 009/149] chore: adjust the configuration loading order --- hub/executor/executor.go | 60 +++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b3e33f98..ec6ee8ff 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -81,13 +81,13 @@ func ApplyConfig(cfg *config.Config, force bool) { updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) + updateGeneral(cfg.General) initInnerTcp() updateDNS(cfg.DNS, cfg.General.IPv6) loadProxyProvider(cfg.Providers) updateProfile(cfg) loadRuleProvider(cfg.RuleProviders) - updateGeneral(cfg.General, force) - updateListeners(cfg.Listeners) + updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) updateTun(cfg.General) updateExperimental(cfg) @@ -134,12 +134,31 @@ func GetGeneral() *config.General { return general } -func updateListeners(listeners map[string]C.InboundListener) { +func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) { tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() natTable := tunnel.NatTable() listener.PatchInboundListeners(listeners, tcpIn, udpIn, natTable, true) + if !force { + return + } + + inbound.SetTfo(general.InboundTfo) + allowLan := general.AllowLan + listener.SetAllowLan(allowLan) + + bindAddress := general.BindAddress + listener.SetBindAddress(bindAddress) + listener.ReCreateHTTP(general.Port, tcpIn) + listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn) + listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn, natTable) + listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn) + listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn, natTable) + listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn) + listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn) + listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) + listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn) } func updateExperimental(c *config.Config) { @@ -304,7 +323,7 @@ func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn()) } -func updateGeneral(general *config.General, force bool) { +func updateGeneral(general *config.General) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) dialer.DisableIPv6 = !general.IPv6 @@ -319,9 +338,9 @@ func updateGeneral(general *config.General, force bool) { } adapter.UnifiedDelay.Store(general.UnifiedDelay) - dialer.DefaultInterface.Store(general.Interface) - - if dialer.DefaultInterface.Load() != "" { + // Avoid reload configuration clean the value, causing traffic loops + if general.Interface != "" && general.Tun.Enable && !general.Tun.AutoDetectInterface { + dialer.DefaultInterface.Store(general.Interface) log.Infoln("Use interface name: %s", general.Interface) } @@ -331,35 +350,8 @@ func updateGeneral(general *config.General, force bool) { } iface.FlushCache() - - if !force { - return - } - geodataLoader := general.GeodataLoader G.SetLoader(geodataLoader) - - allowLan := general.AllowLan - listener.SetAllowLan(allowLan) - - bindAddress := general.BindAddress - listener.SetBindAddress(bindAddress) - - inbound.SetTfo(general.InboundTfo) - - tcpIn := tunnel.TCPIn() - udpIn := tunnel.UDPIn() - natTable := tunnel.NatTable() - - listener.ReCreateHTTP(general.Port, tcpIn) - listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn) - listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn, natTable) - listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn) - listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn, natTable) - listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn) - listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn) - listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) - listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { From b72bd5bb37977ef04886644afc62f742363252db Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 23 Feb 2023 14:13:27 +0800 Subject: [PATCH 010/149] chore: adjust the configuration loading order --- hub/executor/executor.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index ec6ee8ff..57a9ffe8 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -144,6 +144,10 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList return } + if general.Interface == "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) { + dialer.DefaultInterface.Store(general.Interface) + } + inbound.SetTfo(general.InboundTfo) allowLan := general.AllowLan listener.SetAllowLan(allowLan) @@ -339,9 +343,14 @@ func updateGeneral(general *config.General) { adapter.UnifiedDelay.Store(general.UnifiedDelay) // Avoid reload configuration clean the value, causing traffic loops - if general.Interface != "" && general.Tun.Enable && !general.Tun.AutoDetectInterface { + if listener.GetTunConf().Enable && listener.GetTunConf().AutoDetectInterface { + // changed only when the name is specified + // if name is empty, setting delay until after tun loaded + if general.Interface != "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) { + dialer.DefaultInterface.Store(general.Interface) + } + } else { dialer.DefaultInterface.Store(general.Interface) - log.Infoln("Use interface name: %s", general.Interface) } dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) From d5d62a4ffd20235b983842db639bd841fde8ded8 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 23 Feb 2023 20:26:25 +0800 Subject: [PATCH 011/149] chore: change internal tcp traffic type --- adapter/inbound/socket.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 590f64d7..4024ee42 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -34,7 +34,7 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { metadata := &C.Metadata{} metadata.NetWork = C.TCP metadata.Type = C.INNER - metadata.DNSMode = C.DNSMapping + metadata.DNSMode = C.DNSNormal metadata.Host = host metadata.Process = C.ClashName if h, port, err := net.SplitHostPort(dst); err == nil { From a1d008e6f0c62ba0c0272922316c9371d6a0c060 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 23 Feb 2023 23:30:53 +0800 Subject: [PATCH 012/149] chore: add pprof api, when log-level is debug --- hub/hub.go | 3 ++- hub/route/server.go | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/hub/hub.go b/hub/hub.go index 1e925bfe..e4c414fa 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -4,6 +4,7 @@ import ( "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/hub/route" + "github.com/Dreamacro/clash/log" ) type Option func(*config.Config) @@ -43,7 +44,7 @@ func Parse(options ...Option) error { if cfg.General.ExternalController != "" { go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, - cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey) + cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey,cfg.General.LogLevel==log.DEBUG) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/server.go b/hub/route/server.go index 0d6a47ac..2ce4ba8c 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "encoding/json" "net/http" + "runtime/debug" "strings" "time" @@ -13,8 +14,8 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel/statistic" - "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/go-chi/render" "github.com/gorilla/websocket" @@ -43,7 +44,7 @@ func SetUIPath(path string) { } func Start(addr string, tlsAddr string, secret string, - certificat, privateKey string) { + certificat, privateKey string, isDebug bool) { if serverAddr != "" { return } @@ -59,6 +60,17 @@ func Start(addr string, tlsAddr string, secret string, MaxAge: 300, }) r.Use(corsM.Handler) + if isDebug { + r.Mount("/debug", func() http.Handler { + r := chi.NewRouter() + r.Put("/gc", func(w http.ResponseWriter, r *http.Request) { + debug.FreeOSMemory() + }) + handler := middleware.Profiler + r.Mount("/", handler()) + return r + }()) + } r.Group(func(r chi.Router) { r.Use(authentication) r.Get("/", hello) From 75680c5866ebc497016120cda9afc39c3e13a0aa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 24 Feb 2023 09:54:54 +0800 Subject: [PATCH 013/149] chore: use early conn to support real ws 0-rtt --- adapter/outbound/reject.go | 3 +++ adapter/outbound/shadowsocks.go | 4 ++-- adapter/outbound/vmess.go | 10 ++++----- adapter/outboundgroup/fallback.go | 13 ++++++++++- adapter/outboundgroup/loadbalance.go | 28 +++++++++++++++--------- adapter/outboundgroup/urltest.go | 13 ++++++++++- common/callback/callback.go | 25 ++++++++++++++++++++++ common/net/bufconn.go | 12 ++++++++++- component/sniffer/dispatcher.go | 9 ++------ constant/context.go | 4 +++- context/conn.go | 4 ++-- transport/vless/conn.go | 15 ++++++------- tunnel/tunnel.go | 32 ++++++++++++++++++++++++++-- 13 files changed, 132 insertions(+), 40 deletions(-) create mode 100644 common/callback/callback.go diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index 43833238..d5a9c823 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -53,6 +53,9 @@ func (rw *nopConn) Read(b []byte) (int, error) { } func (rw *nopConn) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } return 0, io.EOF } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index ae404eec..2ac1f234 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -103,9 +103,9 @@ func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e } } if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { - return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) + return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil } - return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } // DialContext implements C.ProxyAdapter diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 5da8c8b1..e8220767 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -213,12 +213,12 @@ 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.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } else { - return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } } else { - return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } } @@ -289,9 +289,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o }(c) if v.option.XUDP { - c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } else { - c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } if err != nil { diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 34365d0e..066e8a37 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -7,6 +7,7 @@ import ( "time" "github.com/Dreamacro/clash/adapter/outbound" + "github.com/Dreamacro/clash/common/callback" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -30,11 +31,21 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts . c, err := proxy.DialContext(ctx, metadata, f.Base.DialOptions(opts...)...) if err == nil { c.AppendToChains(f) - f.onDialSuccess() } else { 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) + } + }, + } + return c, err } diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 48bd4994..9a010cf9 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -10,6 +10,7 @@ import ( "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/common/cache" + "github.com/Dreamacro/clash/common/callback" "github.com/Dreamacro/clash/common/murmur3" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" @@ -83,17 +84,24 @@ func jumpHash(key uint64, buckets int32) int32 { // DialContext implements C.ProxyAdapter func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) { proxy := lb.Unwrap(metadata, true) - - defer func() { - if err == nil { - c.AppendToChains(lb) - lb.onDialSuccess() - } else { - lb.onDialFailed(proxy.Type(), err) - } - }() - c, err = proxy.DialContext(ctx, metadata, lb.Base.DialOptions(opts...)...) + + if err == nil { + c.AppendToChains(lb) + } else { + 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) + } + }, + } return } diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 27cef9c6..31eaf4a4 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -6,6 +6,7 @@ import ( "time" "github.com/Dreamacro/clash/adapter/outbound" + "github.com/Dreamacro/clash/common/callback" "github.com/Dreamacro/clash/common/singledo" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" @@ -38,10 +39,20 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts .. c, err = proxy.DialContext(ctx, metadata, u.Base.DialOptions(opts...)...) if err == nil { c.AppendToChains(u) - u.onDialSuccess() } else { 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) + } + }, + } return c, err } diff --git a/common/callback/callback.go b/common/callback/callback.go new file mode 100644 index 00000000..a0f1e717 --- /dev/null +++ b/common/callback/callback.go @@ -0,0 +1,25 @@ +package callback + +import ( + C "github.com/Dreamacro/clash/constant" +) + +type FirstWriteCallBackConn struct { + C.Conn + Callback func(error) + written bool +} + +func (c *FirstWriteCallBackConn) Write(b []byte) (n int, err error) { + defer func() { + if !c.written { + c.written = true + c.Callback(err) + } + }() + return c.Conn.Write(b) +} + +func (c *FirstWriteCallBackConn) Upstream() any { + return c.Conn +} diff --git a/common/net/bufconn.go b/common/net/bufconn.go index ba0ca026..54326cf9 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -12,13 +12,14 @@ var _ ExtendedConn = (*BufferedConn)(nil) type BufferedConn struct { r *bufio.Reader ExtendedConn + peeked bool } func NewBufferedConn(c net.Conn) *BufferedConn { if bc, ok := c.(*BufferedConn); ok { return bc } - return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c)} + return &BufferedConn{bufio.NewReader(c), NewExtendedConn(c), false} } // Reader returns the internal bufio.Reader. @@ -26,11 +27,20 @@ func (c *BufferedConn) Reader() *bufio.Reader { return c.r } +func (c *BufferedConn) Peeked() bool { + return c.peeked +} + // Peek returns the next n bytes without advancing the reader. func (c *BufferedConn) Peek(n int) ([]byte, error) { + c.peeked = true return c.r.Peek(n) } +func (c *BufferedConn) Discard(n int) (discarded int, err error) { + return c.r.Discard(n) +} + func (c *BufferedConn) Read(p []byte) (int, error) { return c.r.Read(p) } diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index f4511b97..97d448ce 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -36,12 +36,7 @@ type SnifferDispatcher struct { parsePureIp bool } -func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) { - bufConn, ok := conn.(*N.BufferedConn) - if !ok { - return - } - +func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) { if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) { port, err := strconv.ParseUint(metadata.DstPort, 10, 16) if err != nil { @@ -74,7 +69,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) { } sd.rwMux.RUnlock() - if host, err := sd.sniffDomain(bufConn, metadata); err != nil { + if host, err := sd.sniffDomain(conn, metadata); err != nil { sd.cacheSniffFailed(metadata) log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort) return diff --git a/constant/context.go b/constant/context.go index e641ed14..da1e4155 100644 --- a/constant/context.go +++ b/constant/context.go @@ -3,6 +3,8 @@ package constant import ( "net" + N "github.com/Dreamacro/clash/common/net" + "github.com/gofrs/uuid" ) @@ -13,7 +15,7 @@ type PlainContext interface { type ConnContext interface { PlainContext Metadata() *Metadata - Conn() net.Conn + Conn() *N.BufferedConn } type PacketConnContext interface { diff --git a/context/conn.go b/context/conn.go index 08bbe3c7..b695ac4d 100644 --- a/context/conn.go +++ b/context/conn.go @@ -12,7 +12,7 @@ import ( type ConnContext struct { id uuid.UUID metadata *C.Metadata - conn net.Conn + conn *N.BufferedConn } func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext { @@ -36,6 +36,6 @@ func (c *ConnContext) Metadata() *C.Metadata { } // Conn implement C.ConnContext Conn -func (c *ConnContext) Conn() net.Conn { +func (c *ConnContext) Conn() *N.BufferedConn { return c.conn } diff --git a/transport/vless/conn.go b/transport/vless/conn.go index aceda463..e063d465 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -7,7 +7,6 @@ import ( "io" "net" "sync" - "time" "github.com/Dreamacro/clash/common/buf" N "github.com/Dreamacro/clash/common/net" @@ -208,12 +207,12 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { } } - go func() { - select { - case <-c.handshake: - case <-time.After(200 * time.Millisecond): - c.sendRequest(nil) - } - }() + //go func() { + // select { + // case <-c.handshake: + // case <-time.After(200 * time.Millisecond): + // c.sendRequest(nil) + // } + //}() return c, nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 695f2945..b9d0e594 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -366,8 +366,20 @@ func handleTCPConn(connCtx C.ConnContext) { return } + conn := connCtx.Conn() if sniffer.Dispatcher.Enable() && sniffingEnable { - sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata) + sniffer.Dispatcher.TCPSniff(conn, metadata) + } + + peekMutex := sync.Mutex{} + if !conn.Peeked() { + peekMutex.Lock() + go func() { + defer peekMutex.Unlock() + _ = conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond)) + _, _ = conn.Peek(1) + _ = conn.SetReadDeadline(time.Time{}) + }() } proxy, rule, err := resolveMetadata(connCtx, metadata) @@ -387,10 +399,26 @@ func handleTCPConn(connCtx C.ConnContext) { } } + var peekBytes []byte + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) defer cancel() remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) { - return proxy.DialContext(ctx, dialMetadata) + remoteConn, err := proxy.DialContext(ctx, dialMetadata) + if err != nil { + return nil, err + } + 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) { if rule == nil { log.Warnln( From 7d524668e02d2ed8d9c4c876ce3b906b055c62e0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 24 Feb 2023 13:53:44 +0800 Subject: [PATCH 014/149] chore: support TFO for outbounds --- adapter/outbound/base.go | 5 ++ adapter/outbound/http.go | 1 + adapter/outbound/shadowsocks.go | 1 + adapter/outbound/shadowsocksr.go | 1 + adapter/outbound/snell.go | 1 + adapter/outbound/socks5.go | 1 + adapter/outbound/trojan.go | 1 + adapter/outbound/vless.go | 1 + adapter/outbound/vmess.go | 1 + component/dialer/dialer.go | 6 +- component/dialer/options.go | 7 ++ component/dialer/tfo.go | 119 +++++++++++++++++++++++++++++++ 12 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 component/dialer/tfo.go diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 24de7d94..d51655d8 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -140,10 +140,15 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option { default: } + if b.tfo { + opts = append(opts, dialer.WithTFO(true)) + } + return opts } type BasicOption struct { + TFO bool `proxy:"tfo,omitempty" group:"tfo,omitempty"` Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"` RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"` IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"` diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 720dc3e1..6a668ebb 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -170,6 +170,7 @@ func NewHttp(option HttpOption) (*Http, error) { name: option.Name, addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Http, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 2ac1f234..6e6a8d0a 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -252,6 +252,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { addr: addr, tp: C.Shadowsocks, udp: option.UDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index e84de879..135e7132 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -163,6 +163,7 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { addr: addr, tp: C.ShadowsocksR, udp: option.UDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 1331b526..d6f1efee 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -167,6 +167,7 @@ func NewSnell(option SnellOption) (*Snell, error) { addr: addr, tp: C.Snell, udp: option.UDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index d40a6bff..cdb89cc2 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -182,6 +182,7 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), tp: C.Socks5, udp: option.UDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 2a8cfe47..beedd614 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -250,6 +250,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { addr: addr, tp: C.Trojan, udp: option.UDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index e46e245d..eef05687 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -510,6 +510,7 @@ func NewVless(option VlessOption) (*Vless, error) { tp: C.Vless, udp: option.UDP, xudp: option.XUDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index e8220767..2ae72069 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -387,6 +387,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { tp: C.Vmess, udp: option.UDP, xudp: option.XUDP, + tfo: option.TFO, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 39e47bde..9ac9d719 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -118,7 +118,11 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return nil, ErrorDisableIPv6 } - return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) + address := net.JoinHostPort(destination.String(), port) + if opt.tfo { + return dialTFO(ctx, *dialer, network, address) + } + return dialer.DialContext(ctx, network, address) } func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { diff --git a/component/dialer/options.go b/component/dialer/options.go index 27adc845..1c4e7bfc 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -18,6 +18,7 @@ type option struct { routingMark int network int prefer int + tfo bool resolver resolver.Resolver } @@ -69,6 +70,12 @@ func WithOnlySingleStack(isIPv4 bool) Option { } } +func WithTFO(tfo bool) Option { + return func(opt *option) { + opt.tfo = tfo + } +} + func WithOption(o option) Option { return func(opt *option) { *opt = o diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go new file mode 100644 index 00000000..2db2e91e --- /dev/null +++ b/component/dialer/tfo.go @@ -0,0 +1,119 @@ +package dialer + +import ( + "context" + "github.com/sagernet/tfo-go" + "io" + "net" + "time" +) + +type tfoConn struct { + net.Conn + closed bool + dialed chan bool + cancel context.CancelFunc + ctx context.Context + dialFn func(ctx context.Context, earlyData []byte) (net.Conn, error) +} + +func (c *tfoConn) Dial(earlyData []byte) (err error) { + c.Conn, err = c.dialFn(c.ctx, earlyData) + if err != nil { + return + } + c.dialed <- true + return err +} + +func (c *tfoConn) Read(b []byte) (n int, err error) { + if c.closed { + return 0, io.ErrClosedPipe + } + if c.Conn == nil { + select { + case <-c.ctx.Done(): + return 0, io.ErrUnexpectedEOF + case <-c.dialed: + } + } + return c.Conn.Read(b) +} + +func (c *tfoConn) Write(b []byte) (n int, err error) { + if c.closed { + return 0, io.ErrClosedPipe + } + if c.Conn == nil { + if err := c.Dial(b); err != nil { + return 0, err + } + return len(b), nil + } + + return c.Conn.Write(b) +} + +func (c *tfoConn) Close() error { + c.closed = true + c.cancel() + if c.Conn == nil { + return nil + } + return c.Conn.Close() +} + +func (c *tfoConn) LocalAddr() net.Addr { + if c.Conn == nil { + return nil + } + return c.Conn.LocalAddr() +} + +func (c *tfoConn) RemoteAddr() net.Addr { + if c.Conn == nil { + return nil + } + return c.Conn.RemoteAddr() +} + +func (c *tfoConn) SetDeadline(t time.Time) error { + if err := c.SetReadDeadline(t); err != nil { + return err + } + return c.SetWriteDeadline(t) +} + +func (c *tfoConn) SetReadDeadline(t time.Time) error { + if c.Conn == nil { + return nil + } + return c.Conn.SetReadDeadline(t) +} + +func (c *tfoConn) SetWriteDeadline(t time.Time) error { + if c.Conn == nil { + return nil + } + return c.Conn.SetWriteDeadline(t) +} + +func (c *tfoConn) Upstream() any { + if c.Conn == nil { // ensure return a nil interface not an interface with nil value + return nil + } + return c.Conn +} + +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} + return &tfoConn{ + dialed: make(chan bool, 1), + cancel: cancel, + ctx: ctx, + dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { + return dialer.DialContext(ctx, network, address, earlyData) + }, + }, nil +} From 8f0c61ed14b8019fe851edda3e76c63647249b66 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 24 Feb 2023 14:02:20 +0800 Subject: [PATCH 015/149] fix: tuic missing routing mark --- adapter/outbound/tuic.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 5b7bde6e..b6335fa8 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -216,6 +216,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { udp: true, tfo: option.FastOpen, iface: option.Interface, + rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, } From 880664c6ab9df410df0ceae85ea221484c175b8c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 24 Feb 2023 14:19:50 +0800 Subject: [PATCH 016/149] fix: tunnel's inboundTFO missing --- hub/executor/executor.go | 3 ++- listener/tunnel/tcp.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 57a9ffe8..916f17c7 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -148,7 +148,6 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList dialer.DefaultInterface.Store(general.Interface) } - inbound.SetTfo(general.InboundTfo) allowLan := general.AllowLan listener.SetAllowLan(allowLan) @@ -341,6 +340,8 @@ func updateGeneral(general *config.General) { log.Infoln("Use tcp concurrent") } + inbound.SetTfo(general.InboundTfo) + adapter.UnifiedDelay.Store(general.UnifiedDelay) // Avoid reload configuration clean the value, causing traffic loops if listener.GetTunConf().Enable && listener.GetTunConf().AutoDetectInterface { diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index bf278c1c..c1d896ad 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -41,7 +41,7 @@ func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext, additions . } func New(addr, target, proxy string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { - l, err := net.Listen("tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } From 5bfad04b4121523d8d1ad1927591a10393f152f4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 24 Feb 2023 14:58:01 +0800 Subject: [PATCH 017/149] fix: checkTunName mistake --- listener/sing_tun/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 63d475cf..3abef12d 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -77,7 +77,7 @@ func checkTunName(tunName string) (ok bool) { if len(tunName) <= 4 { return false } - if tunName[:4] == "utun" { + if tunName[:4] != "utun" { return false } if _, parseErr := strconv.ParseInt(tunName[4:], 10, 16); parseErr != nil { From 81722610d50904a4aa88eaa8f3f12ee97bcbc5ac Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Sat, 25 Feb 2023 13:12:19 +0800 Subject: [PATCH 018/149] feat: Support VLESS XTLS Vision (#406) --- adapter/outbound/vless.go | 8 +- common/buf/sing.go | 6 + transport/vless/conn.go | 352 +++++++++++++++++++++++++++++++++----- transport/vless/filter.go | 79 +++++++++ transport/vless/vision.go | 69 ++++++++ transport/vless/vless.go | 1 + transport/vless/xtls.go | 5 + 7 files changed, 477 insertions(+), 43 deletions(-) create mode 100644 transport/vless/filter.go create mode 100644 transport/vless/vision.go diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index eef05687..010af23c 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -171,7 +171,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) { host, _, _ := net.SplitHostPort(v.addr) - if v.isXTLSEnabled() && !isH2 { + if v.isLegacyXTLSEnabled() && !isH2 { xtlsOpts := vless.XTLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, @@ -206,8 +206,8 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) return conn, nil } -func (v *Vless) isXTLSEnabled() bool { - return v.client.Addons != nil +func (v *Vless) isLegacyXTLSEnabled() bool { + return v.client.Addons != nil && v.client.Addons.Flow != vless.XRV } // DialContext implements C.ProxyAdapter @@ -479,7 +479,7 @@ func NewVless(option VlessOption) (*Vless, error) { if option.Network != "ws" && len(option.Flow) >= 16 { option.Flow = option.Flow[:16] switch option.Flow { - case vless.XRO, vless.XRD, vless.XRS: + case vless.XRO, vless.XRD, vless.XRS, vless.XRV: addons = &vless.Addons{ Flow: option.Flow, } diff --git a/common/buf/sing.go b/common/buf/sing.go index b5e015f5..f86b5755 100644 --- a/common/buf/sing.go +++ b/common/buf/sing.go @@ -5,9 +5,15 @@ import ( "github.com/sagernet/sing/common/buf" ) +const BufferSize = buf.BufferSize + type Buffer = buf.Buffer +var New = buf.New +var StackNew = buf.StackNew var StackNewSize = buf.StackNewSize +var With = buf.With + var KeepAlive = common.KeepAlive //go:norace diff --git a/transport/vless/conn.go b/transport/vless/conn.go index e063d465..eae8868e 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -1,23 +1,33 @@ package vless import ( + "bytes" + "crypto/subtle" + gotls "crypto/tls" "encoding/binary" "errors" "fmt" "io" "net" + "reflect" "sync" + "unsafe" "github.com/Dreamacro/clash/common/buf" N "github.com/Dreamacro/clash/common/net" + tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/log" "github.com/gofrs/uuid" + utls "github.com/sagernet/utls" xtls "github.com/xtls/go" "google.golang.org/protobuf/proto" ) type Conn struct { - N.ExtendedConn + N.ExtendedWriter + N.ExtendedReader + net.Conn dst *DstAddr id *uuid.UUID addons *Addons @@ -26,30 +36,143 @@ type Conn struct { handshake chan struct{} handshakeMutex sync.Mutex err error + + tlsConn net.Conn + input *bytes.Reader + rawInput *bytes.Buffer + + packetsToFilter int + isTLS bool + isTLS12orAbove bool + enableXTLS bool + cipher uint16 + remainingServerHello uint16 + readRemainingContent int + readRemainingPadding int + readProcess bool + readFilterUUID bool + readLastCommand byte + writeFilterApplicationData bool + writeDirect bool } func (vc *Conn) Read(b []byte) (int, error) { if vc.received { - return vc.ExtendedConn.Read(b) + if vc.readProcess { + buffer := buf.With(b) + err := vc.ReadBuffer(buffer) + return buffer.Len(), err + } + return vc.ExtendedReader.Read(b) } if err := vc.recvResponse(); err != nil { return 0, err } vc.received = true - return vc.ExtendedConn.Read(b) + return vc.Read(b) } func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { if vc.received { - return vc.ExtendedConn.ReadBuffer(buffer) + toRead := buffer.FreeBytes() + if vc.readRemainingContent > 0 { + if vc.readRemainingContent < buffer.FreeLen() { + toRead = toRead[:vc.readRemainingContent] + } + n, err := vc.ExtendedReader.Read(toRead) + buffer.Truncate(n) + vc.readRemainingContent -= n + vc.FilterTLS(toRead) + return err + } + if vc.readRemainingPadding > 0 { + _, err := io.CopyN(io.Discard, vc.ExtendedReader, int64(vc.readRemainingPadding)) + if err != nil { + return err + } + vc.readRemainingPadding = 0 + } + if vc.readProcess { + switch vc.readLastCommand { + case commandPaddingContinue: + //if vc.isTLS || vc.packetsToFilter > 0 { + headerUUIDLen := 0 + if vc.readFilterUUID { + headerUUIDLen = uuid.Size + } + var header []byte + if need := headerUUIDLen + paddingHeaderLen; buffer.FreeLen() < need { + header = make([]byte, need) + } else { + header = buffer.FreeBytes()[:need] + } + _, err := io.ReadFull(vc.ExtendedReader, header) + if err != nil { + return err + } + pos := 0 + if vc.readFilterUUID { + vc.readFilterUUID = false + pos = uuid.Size + if subtle.ConstantTimeCompare(vc.id.Bytes(), header[:uuid.Size]) != 1 { + err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s", + uuid.FromBytesOrNil(header[:uuid.Size]).String()) + log.Errorln(err.Error()) + return err + } + } + vc.readLastCommand = header[pos] + vc.readRemainingContent = int(binary.BigEndian.Uint16(header[pos+1:])) + vc.readRemainingPadding = int(binary.BigEndian.Uint16(header[pos+3:])) + log.Debugln("XTLS Vision read padding: command=%d, payloadLen=%d, paddingLen=%d", + vc.readLastCommand, vc.readRemainingContent, vc.readRemainingPadding) + return vc.ReadBuffer(buffer) + //} + case commandPaddingEnd: + vc.readProcess = false + return vc.ReadBuffer(buffer) + case commandPaddingDirect: + if vc.input != nil { + _, err := buffer.ReadFrom(vc.input) + if err != nil { + return err + } + if vc.input.Len() == 0 { + vc.input = nil + } + if buffer.IsFull() { + return nil + } + } + if vc.rawInput != nil { + _, err := buffer.ReadFrom(vc.rawInput) + if err != nil { + return err + } + if vc.rawInput.Len() == 0 { + vc.rawInput = nil + } + } + if vc.input == nil && vc.rawInput == nil { + vc.readProcess = false + vc.ExtendedReader = N.NewExtendedReader(vc.Conn) + log.Debugln("XTLS Vision direct read start") + } + default: + err := fmt.Errorf("XTLS Vision read unknown command: %d", vc.readLastCommand) + log.Debugln(err.Error()) + return err + } + } + return vc.ExtendedReader.ReadBuffer(buffer) } if err := vc.recvResponse(); err != nil { return err } vc.received = true - return vc.ExtendedConn.ReadBuffer(buffer) + return vc.ReadBuffer(buffer) } func (vc *Conn) Write(p []byte) (int, error) { @@ -66,7 +189,19 @@ func (vc *Conn) Write(p []byte) (int, error) { return 0, vc.err } } - return vc.ExtendedConn.Write(p) + if vc.writeFilterApplicationData { + _buffer := buf.StackNew() + defer buf.KeepAlive(_buffer) + buffer := buf.Dup(_buffer) + defer buffer.Release() + buffer.Write(p) + err := vc.WriteBuffer(buffer) + if err != nil { + return 0, err + } + return len(p), nil + } + return vc.ExtendedWriter.Write(p) } func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { @@ -80,7 +215,57 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { return vc.err } } - return vc.ExtendedConn.WriteBuffer(buffer) + if vc.writeFilterApplicationData && vc.isTLS { + buffer2 := ReshapeBuffer(buffer) + defer buffer2.Release() + vc.FilterTLS(buffer.Bytes()) + command := commandPaddingContinue + if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 { + command = commandPaddingEnd + if vc.enableXTLS { + command = commandPaddingDirect + vc.writeDirect = true + } + vc.writeFilterApplicationData = false + } + ApplyPadding(buffer, command, nil) + err := vc.ExtendedWriter.WriteBuffer(buffer) + if err != nil { + return err + } + if vc.writeDirect { + vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn) + log.Debugln("XTLS Vision direct write start") + //time.Sleep(10 * time.Millisecond) + } + if buffer2 != nil { + if vc.writeDirect { + return vc.ExtendedWriter.WriteBuffer(buffer2) + } + vc.FilterTLS(buffer2.Bytes()) + command = commandPaddingContinue + if buffer2.Len() > 6 && bytes.Equal(buffer2.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 { + command = commandPaddingEnd + if vc.enableXTLS { + command = commandPaddingDirect + vc.writeDirect = true + } + vc.writeFilterApplicationData = false + } + ApplyPadding(buffer2, command, nil) + err = vc.ExtendedWriter.WriteBuffer(buffer2) + if vc.writeDirect { + vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn) + log.Debugln("XTLS Vision direct write start") + //time.Sleep(10 * time.Millisecond) + } + } + return err + } + /*if vc.writeDirect { + log.Debugln("XTLS Vision Direct write, payloadLen=%d", buffer.Len()) + }*/ + return vc.ExtendedWriter.WriteBuffer(buffer) } func (vc *Conn) sendRequest(p []byte) bool { @@ -96,9 +281,6 @@ func (vc *Conn) sendRequest(p []byte) bool { } defer close(vc.handshake) - requestLen := 1 // protocol version - requestLen += 16 // UUID - requestLen += 1 // addons length var addonsBytes []byte if vc.addons != nil { addonsBytes, vc.err = proto.Marshal(vc.addons) @@ -106,19 +288,32 @@ func (vc *Conn) sendRequest(p []byte) bool { return true } } - requestLen += len(addonsBytes) - requestLen += 1 // command - if !vc.dst.Mux { - requestLen += 2 // port - requestLen += 1 // addr type - requestLen += len(vc.dst.Addr) - } - requestLen += len(p) + isVision := vc.IsXTLSVisionEnabled() - _buffer := buf.StackNewSize(requestLen) - defer buf.KeepAlive(_buffer) - buffer := buf.Dup(_buffer) - defer buffer.Release() + var buffer *buf.Buffer + if isVision { + _buffer := buf.StackNew() + defer buf.KeepAlive(_buffer) + buffer = buf.Dup(_buffer) + defer buffer.Release() + } else { + requestLen := 1 // protocol version + requestLen += 16 // UUID + requestLen += 1 // addons length + requestLen += len(addonsBytes) + requestLen += 1 // command + if !vc.dst.Mux { + requestLen += 2 // port + requestLen += 1 // addr type + requestLen += len(vc.dst.Addr) + } + requestLen += len(p) + + _buffer := buf.StackNewSize(requestLen) + defer buf.KeepAlive(_buffer) + buffer = buf.Dup(_buffer) + defer buffer.Release() + } buf.Must( buffer.WriteByte(Version), // protocol version @@ -143,15 +338,51 @@ func (vc *Conn) sendRequest(p []byte) bool { ) } - buf.Must(buf.Error(buffer.Write(p))) + if isVision && !vc.dst.UDP && !vc.dst.Mux { + if len(p) == 0 { + vc.packetsToFilter = 0 + vc.writeFilterApplicationData = false + WriteWithPadding(buffer, nil, commandPaddingEnd, vc.id) + } else { + vc.FilterTLS(p) + if vc.isTLS { + WriteWithPadding(buffer, p, commandPaddingContinue, vc.id) + } else { + buf.Must(buf.Error(buffer.Write(p))) + vc.readProcess = false + vc.writeFilterApplicationData = false + vc.packetsToFilter = 0 + } + } + } else { + buf.Must(buf.Error(buffer.Write(p))) + } - _, vc.err = vc.ExtendedConn.Write(buffer.Bytes()) + _, vc.err = vc.ExtendedWriter.Write(buffer.Bytes()) + if vc.err != nil { + return true + } + if isVision { + switch underlying := vc.tlsConn.(type) { + case *gotls.Conn: + if underlying.ConnectionState().Version != gotls.VersionTLS13 { + vc.err = ErrNotTLS13 + } + case *utls.UConn: + if underlying.ConnectionState().Version != utls.VersionTLS13 { + vc.err = ErrNotTLS13 + } + default: + vc.err = fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, vc.addons.Flow) + } + vc.tlsConn = nil + } return true } func (vc *Conn) recvResponse() error { var buf [1]byte - _, vc.err = io.ReadFull(vc.ExtendedConn, buf[:]) + _, vc.err = io.ReadFull(vc.ExtendedReader, buf[:]) if vc.err != nil { return vc.err } @@ -160,30 +391,46 @@ func (vc *Conn) recvResponse() error { return errors.New("unexpected response version") } - _, vc.err = io.ReadFull(vc.ExtendedConn, buf[:]) + _, vc.err = io.ReadFull(vc.ExtendedReader, buf[:]) if vc.err != nil { return vc.err } length := int64(buf[0]) if length != 0 { // addon data length > 0 - io.CopyN(io.Discard, vc.ExtendedConn, length) // just discard + io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard } return nil } +func (vc *Conn) FrontHeadroom() int { + if vc.IsXTLSVisionEnabled() { + return paddingHeaderLen + } + return 0 +} + func (vc *Conn) Upstream() any { - return vc.ExtendedConn + if vc.tlsConn == nil { + return vc.Conn + } + return vc.tlsConn +} + +func (vc *Conn) IsXTLSVisionEnabled() bool { + return vc.addons != nil && vc.addons.Flow == XRV } // newConn return a Conn instance func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { c := &Conn{ - ExtendedConn: N.NewExtendedConn(conn), - id: client.uuid, - dst: dst, - handshake: make(chan struct{}), + ExtendedReader: N.NewExtendedReader(conn), + ExtendedWriter: N.NewExtendedWriter(conn), + Conn: conn, + id: client.uuid, + dst: dst, + handshake: make(chan struct{}), } if !dst.UDP && client.Addons != nil { @@ -204,15 +451,42 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { } else { return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow) } + case XRV: + c.packetsToFilter = 6 + c.readProcess = true + c.readFilterUUID = true + c.writeFilterApplicationData = true + c.addons = client.Addons + var t reflect.Type + var p uintptr + switch underlying := conn.(type) { + case *gotls.Conn: + c.Conn = underlying.NetConn() + c.tlsConn = underlying + t = reflect.TypeOf(underlying).Elem() + p = uintptr(unsafe.Pointer(underlying)) + case *utls.UConn: + c.Conn = underlying.NetConn() + c.tlsConn = underlying + t = reflect.TypeOf(underlying.Conn).Elem() + p = uintptr(unsafe.Pointer(underlying.Conn)) + case *tlsC.UConn: + c.Conn = underlying.NetConn() + c.tlsConn = underlying.UConn + t = reflect.TypeOf(underlying.Conn).Elem() + p = uintptr(unsafe.Pointer(underlying.Conn)) + default: + return nil, fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, client.Addons.Flow) + } + i, _ := t.FieldByName("input") + r, _ := t.FieldByName("rawInput") + c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) + c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) + if _, ok := c.Conn.(*net.TCPConn); !ok { + log.Debugln("XTLS underlying conn is not *net.TCPConn, got %s", reflect.TypeOf(conn).Name()) + } } } - //go func() { - // select { - // case <-c.handshake: - // case <-time.After(200 * time.Millisecond): - // c.sendRequest(nil) - // } - //}() return c, nil } diff --git a/transport/vless/filter.go b/transport/vless/filter.go new file mode 100644 index 00000000..15d595bc --- /dev/null +++ b/transport/vless/filter.go @@ -0,0 +1,79 @@ +package vless + +import ( + "bytes" + "encoding/binary" + + log "github.com/sirupsen/logrus" +) + +var ( + tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04} + tlsClientHandshakeStart = []byte{0x16, 0x03} + tlsServerHandshakeStart = []byte{0x16, 0x03, 0x03} + tlsApplicationDataStart = []byte{0x17, 0x03, 0x03} + + tls13CipherSuiteMap = map[uint16]string{ + 0x1301: "TLS_AES_128_GCM_SHA256", + 0x1302: "TLS_AES_256_GCM_SHA384", + 0x1303: "TLS_CHACHA20_POLY1305_SHA256", + 0x1304: "TLS_AES_128_CCM_SHA256", + 0x1305: "TLS_AES_128_CCM_8_SHA256", + } +) + +const ( + tlsHandshakeTypeClientHello byte = 0x01 + tlsHandshakeTypeServerHello byte = 0x02 +) + +func (vc *Conn) FilterTLS(p []byte) (index int) { + if vc.packetsToFilter <= 0 { + return 0 + } + lenP := len(p) + vc.packetsToFilter -= 1 + if index = bytes.Index(p, tlsServerHandshakeStart); index != -1 { + if lenP >= index+5 && p[index+5] == tlsHandshakeTypeServerHello { + vc.remainingServerHello = binary.BigEndian.Uint16(p[index+3:]) + 5 + vc.isTLS = true + vc.isTLS12orAbove = true + if lenP-index >= 79 && vc.remainingServerHello >= 79 { + sessionIDLen := int(p[index+43]) + vc.cipher = binary.BigEndian.Uint16(p[index+43+sessionIDLen+1:]) + } + } + } else if index = bytes.Index(p, tlsClientHandshakeStart); index != -1 { + if lenP >= index+5 && p[index+5] == tlsHandshakeTypeClientHello { + vc.isTLS = true + } + } + + if vc.remainingServerHello > 0 { + end := vc.remainingServerHello + vc.remainingServerHello -= end + if end > uint16(lenP) { + end = uint16(lenP) + } + if bytes.Contains(p[index:end], tls13SupportedVersions) { + // TLS 1.3 Client Hello + cs, ok := tls13CipherSuiteMap[vc.cipher] + if ok && cs != "TLS_AES_128_CCM_8_SHA256" { + vc.enableXTLS = true + } + log.Debugln("XTLS Vision found TLS 1.3, packetLength=", lenP, ", CipherSuite=", cs) + vc.packetsToFilter = 0 + return + } else if vc.remainingServerHello < 0 { + log.Debugln("XTLS Vision found TLS 1.2, packetLength=", lenP) + vc.packetsToFilter = 0 + return + } + log.Debugln("XTLS Vision found inconclusive server hello, packetLength=", lenP, + ", remainingServerHelloBytes=", vc.remainingServerHello) + } + if vc.packetsToFilter <= 0 { + log.Debugln("XTLS Vision stop filtering") + } + return +} diff --git a/transport/vless/vision.go b/transport/vless/vision.go new file mode 100644 index 00000000..f87a6870 --- /dev/null +++ b/transport/vless/vision.go @@ -0,0 +1,69 @@ +package vless + +import ( + "bytes" + "encoding/binary" + "math/rand" + + "github.com/Dreamacro/clash/common/buf" + "github.com/Dreamacro/clash/log" + + "github.com/gofrs/uuid" +) + +const ( + paddingHeaderLen = 1 + 2 + 2 // =5 + + commandPaddingContinue byte = 0x00 + commandPaddingEnd byte = 0x01 + commandPaddingDirect byte = 0x02 +) + +func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID) { + contentLen := int32(len(p)) + var paddingLen int32 + if contentLen < 900 { + paddingLen = rand.Int31n(500) + 900 - contentLen + } + + if userUUID != nil { // unnecessary, but keep the same with Xray + buffer.Write(userUUID.Bytes()) + } + buffer.WriteByte(command) + binary.BigEndian.PutUint16(buffer.Extend(2), uint16(contentLen)) + binary.BigEndian.PutUint16(buffer.Extend(2), uint16(paddingLen)) + buffer.Write(p) + buffer.Extend(int(paddingLen)) + log.Debugln("XTLS Vision write padding1: command=%v, payloadLen=%v, paddingLen=%v", command, contentLen, paddingLen) +} + +func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID) { + contentLen := int32(buffer.Len()) + var paddingLen int32 + if contentLen < 900 { + paddingLen = rand.Int31n(500) + 900 - contentLen + } + + binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen)) + binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen)) + buffer.ExtendHeader(1)[0] = command + if userUUID != nil { // unnecessary, but keep the same with Xray + copy(buffer.ExtendHeader(uuid.Size), userUUID.Bytes()) + } + buffer.Extend(int(paddingLen)) + log.Debugln("XTLS Vision write padding2: command=%d, payloadLen=%d, paddingLen=%d", command, contentLen, paddingLen) +} + +func ReshapeBuffer(buffer *buf.Buffer) *buf.Buffer { + if buffer.Len() <= buf.BufferSize-paddingHeaderLen { + return nil + } + cutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart) + if cutAt == -1 { + cutAt = buf.BufferSize / 2 + } + buffer2 := buf.New() + buffer2.Write(buffer.From(cutAt)) + buffer.Truncate(cutAt) + return buffer2 +} diff --git a/transport/vless/vless.go b/transport/vless/vless.go index 4b101703..6989374c 100644 --- a/transport/vless/vless.go +++ b/transport/vless/vless.go @@ -12,6 +12,7 @@ const ( XRO = "xtls-rprx-origin" XRD = "xtls-rprx-direct" XRS = "xtls-rprx-splice" + XRV = "xtls-rprx-vision" Version byte = 0 // protocol version. preview version is 0 ) diff --git a/transport/vless/xtls.go b/transport/vless/xtls.go index a1aea44f..3a319568 100644 --- a/transport/vless/xtls.go +++ b/transport/vless/xtls.go @@ -2,6 +2,7 @@ package vless import ( "context" + "errors" "net" tlsC "github.com/Dreamacro/clash/component/tls" @@ -9,6 +10,10 @@ import ( xtls "github.com/xtls/go" ) +var ( + ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection") +) + type XTLSConfig struct { Host string SkipCertVerify bool From bce3aeb2188dbac377af6f879f34825a81c9d0f8 Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Sat, 25 Feb 2023 15:00:21 +0800 Subject: [PATCH 019/149] fix: Vision disable filter for non-TLS connections --- transport/vless/conn.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index eae8868e..fd1767cf 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -340,15 +340,20 @@ func (vc *Conn) sendRequest(p []byte) bool { if isVision && !vc.dst.UDP && !vc.dst.Mux { if len(p) == 0 { - vc.packetsToFilter = 0 - vc.writeFilterApplicationData = false WriteWithPadding(buffer, nil, commandPaddingEnd, vc.id) + + // disable XTLS + vc.readProcess = false + vc.writeFilterApplicationData = false + vc.packetsToFilter = 0 } else { vc.FilterTLS(p) if vc.isTLS { WriteWithPadding(buffer, p, commandPaddingContinue, vc.id) } else { buf.Must(buf.Error(buffer.Write(p))) + + // disable XTLS vc.readProcess = false vc.writeFilterApplicationData = false vc.packetsToFilter = 0 From 22726c1de847642591b67e9a45ffa3a05af126eb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 25 Feb 2023 15:05:24 +0800 Subject: [PATCH 020/149] fix: add version of shadow-tls plugin in docs/config.yaml --- docs/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/config.yaml b/docs/config.yaml index 35e85ecc..e1fc5d2e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -309,6 +309,7 @@ proxies: plugin-opts: host: "cloud.tencent.com" password: "shadow_tls_password" + version: 2 # support 1/2/3 # vmess # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none From e6377eac9bbb9804b85672bc640b899143ec457a Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 25 Feb 2023 17:20:38 +0800 Subject: [PATCH 021/149] chore: adjust config.yaml. --- docs/config.yaml | 458 ++++++++++++++++++++++++----------------------- 1 file changed, 236 insertions(+), 222 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index e1fc5d2e..f6e9502e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -7,22 +7,17 @@ mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口 # tproxy-port: 7893 allow-lan: true # 允许局域网连接 -bind-address: "*" # 绑定IP地址,仅作用于 allow-lan 为 true,'*'表示所有地址 +bind-address: "*" # 绑定 IP 地址,仅作用于 allow-lan 为 true,'*'表示所有地址 -# find-process-mode has 3 values: always, strict, off +# find-process-mode has 3 values:always, strict, off # - always, 开启,强制匹配所有进程 -# - strict, 默认,由clash判断是否开启 +# - strict, 默认,由 clash 判断是否开启 # - off, 不匹配进程,推荐在路由器上使用此模式 find-process-mode: strict -# global-client-fingerprint:全局TLS指纹,优先低于proxy内的 client-fingerprint -# accepts "chrome","firefox","safari","ios","random","none" options. -# Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan. -global-client-fingerprint: chrome - mode: rule -#自定义 geox-url +#自定义 geodata url geox-url: geoip: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" geosite: "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" @@ -32,16 +27,25 @@ log-level: debug # 日志等级 silent/error/warning/info/debug ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录 +tls: + certificate: string # 证书 PEM 格式,或者 证书的路径 + private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 + external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 -# secret: "123456" # `Authorization: Bearer ${secret}` +# secret: "123456" # `Authorization:Bearer ${secret}` -# tcp-concurrent: true # TCP并发连接所有IP, 将使用最快握手的TCP -external-ui: /path/to/ui/folder # 配置WEB UI目录,使用http://{{external-controller}}/ui 访问 +# tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP +external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{external-controller}}/ui 访问 # interface-name: en0 # 设置出口网卡 -# routing-mark: 6666 # 配置 fwmark 仅用于Linux +# 全局 TLS 指纹,优先低于 proxy 内的 client-fingerprint +# 可选: "chrome","firefox","safari","ios","random","none" options. +# Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan. +global-client-fingerprint: chrome + +# routing-mark:6666 # 配置 fwmark 仅用于 Linux experimental: # 类似于 /etc/hosts, 仅支持配置单个 IP @@ -50,6 +54,13 @@ hosts: # '.dev': 127.0.0.1 # 'alpha.clash.dev': '::1' +profile: + # 存储 select 选择记录 + store-selected: false + + # 持久化 fake-ip + store-fake-ip: true + # Tun 配置 tun: enable: false @@ -75,10 +86,10 @@ tun: #- 1000 # exclude_uid_range: # 排除路由的的用户范围 # - 1000-99999 - + # Android 用户和应用规则仅在 Android 下被支持 # 并且需要 auto_route - + # include_android_user: # 限制被路由的 Android 用户 # - 0 # - 10 @@ -105,15 +116,13 @@ sniffer: # 是否使用嗅探结果作为实际访问,默认 true # 全局配置,优先级低于 sniffer.sniff 实际配置 override-destination: false - sniff: - # TLS 默认如果不配置 ports 默认嗅探 443 + sniff: # TLS 默认如果不配置 ports 默认嗅探 443 TLS: # ports: [443, 8443] - + # 默认嗅探 80 - HTTP: - # 需要嗅探的端口 - + HTTP: # 需要嗅探的端口 + ports: [80, 8080-8880] # 可覆盖 sniffer.override-destination override-destination: true @@ -128,7 +137,7 @@ sniffer: - tls - http # 强制对此域名进行嗅探 - + # 仅对白名单中的端口进行嗅探,默认为 443,80 # 已废弃,若 sniffer.sniff 配置则此项无效 port-whitelist: @@ -136,27 +145,8 @@ sniffer: - "443" # - 8000-9999 -# shadowsocks,vmess 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) -# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 -# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 -# tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) -#tuic-server: -# enable: true -# listen: 127.0.0.1:10443 -# token: -# - TOKEN -# certificate: ./server.crt -# private-key: ./server.key -# congestion-controller: bbr -# max-idle-time: 15000 -# authentication-timeout: 1000 -# alpn: -# - h3 -# max-udp-relay-packet-size: 1500 - -tunnels: - # one line config +tunnels: # one line config - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn # full yaml config @@ -165,12 +155,6 @@ tunnels: target: target.com proxy: proxy -profile: - # 存储select选择记录 - store-selected: false - - # 持久化fake-ip - store-fake-ip: true # DNS配置 dns: @@ -178,7 +162,7 @@ dns: prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 - + # 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名 # 只能使用纯 IP 地址,可使用加密 DNS default-nameserver: @@ -187,16 +171,16 @@ dns: - tls://1.12.12.12:853 - tls://223.5.5.5:853 enhanced-mode: fake-ip # or redir-host - + fake-ip-range: 198.18.0.1/16 # fake-ip 池设置 - + # use-hosts: true # 查询 hosts - + # 配置不使用fake-ip的域名 # fake-ip-filter: # - '*.lan' # - localhost.ptlogin2.qq.com - + # DNS主要域名配置 # 支持 UDP,TCP,DoT,DoH,DoQ # 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS @@ -210,20 +194,20 @@ dns: - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC # - '8.8.8.8#en0' # 兼容指定DNS出口网卡 - + # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 # 当不是 CN,则使用 fallback 中的 DNS 查询结果 # 确保配置 fallback 时能够正常查询 # fallback: # - tcp://1.1.1.1 # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 - + # 专用于节点域名解析的 DNS 服务器,非必要配置项 # 配置服务器若查询失败将使用 nameserver,非并发查询 # proxy-server-nameserver: # - https://dns.google/dns-query # - tls://one.one.one.one - + # 配置 fallback 使用条件 # fallback-filter: # geoip: true # 配置是否使用 geoip @@ -238,14 +222,53 @@ dns: # - '+.google.com' # - '+.facebook.com' # - '+.youtube.com' - + # 配置查询域名使用的 DNS 服务器 - nameserver-policy: - # 'www.baidu.com': '114.114.114.114' + nameserver-policy: # 'www.baidu.com': '114.114.114.114' # '+.internal.crop.com': '10.0.0.1' - "geosite:cn": "https://doh.pub/dns-query" - "www.baidu.com": [https://doh.pub/dns-query,https://dns.alidns.com/dns-query] -proxies: + "geosite:cn": + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + "www.baidu.com": [https://doh.pub/dns-query, https://dns.alidns.com/dns-query] + +proxies: # socks5 + - name: "socks" + type: socks5 + server: server + port: 443 + # username: username + # password: password + # tls: true + # fingerprint: xxxx + # skip-cert-verify: true + # udp: true + # ip-version: ipv6 + + # http + - name: "http" + type: http + server: server + port: 443 + # username: username + # password: password + # tls: true # https + # skip-cert-verify: true + # sni: custom.com + # fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints + # ip-version: dual + + # Snell + # Beware that there's currently no UDP support yet + - name: "snell" + type: snell + server: server + port: 44046 + psk: yourpsk + # version: 2 + # obfs-opts: + # mode: http # or tls + # host: bing.com + # Shadowsocks # cipher支持: # aes-128-gcm aes-192-gcm aes-256-gcm @@ -268,6 +291,7 @@ proxies: # UDP 则为双栈解析,获取结果中的第一个 IPv4 # ipv6-prefer 同 ipv4-prefer # 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效 + - name: "ss2" type: ss server: server @@ -278,7 +302,7 @@ proxies: plugin-opts: mode: tls # or http # host: bing.com - + - name: "ss3" type: ss server: server @@ -288,17 +312,17 @@ proxies: plugin: v2ray-plugin plugin-opts: mode: websocket # no QUIC now - # tls: true # wss - # 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 - # 配置指纹将实现 SSL Pining 效果 - # fingerprint: xxxx - # skip-cert-verify: true - # host: bing.com - # path: "/" - # mux: true - # headers: - # custom: value - + # tls: true # wss + # 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 + # 配置指纹将实现 SSL Pining 效果 + # fingerprint: xxxx + # skip-cert-verify: true + # host: bing.com + # path: "/" + # mux: true + # headers: + # custom: value + - name: "ss4" type: ss server: server @@ -310,7 +334,7 @@ proxies: host: "cloud.tencent.com" password: "shadow_tls_password" version: 2 # support 1/2/3 - + # vmess # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none - name: "vmess" @@ -333,7 +357,7 @@ proxies: # Host: v2ray.com # max-early-data: 2048 # early-data-header-name: Sec-WebSocket-Protocol - + - name: "vmess-h2" type: vmess server: server @@ -349,7 +373,7 @@ proxies: - http.example.com - http-alt.example.com path: / - + - name: "vmess-http" type: vmess server: server @@ -360,15 +384,15 @@ proxies: # udp: true # network: http # http-opts: - # # method: "GET" - # # path: - # # - '/' - # # - '/video' - # # headers: - # # Connection: - # # - keep-alive + # method: "GET" + # path: + # - '/' + # - '/video' + # headers: + # Connection: + # - keep-alive # ip-version: ipv4 # 设置使用 IP 类型偏好,可选:ipv4,ipv6,dual,默认值:dual - + - name: vmess-grpc server: server port: 443 @@ -384,100 +408,7 @@ proxies: grpc-opts: grpc-service-name: "example" # ip-version: ipv4 - - # socks5 - - name: "socks" - type: socks5 - server: server - port: 443 - # username: username - # password: password - # tls: true - # fingerprint: xxxx - # skip-cert-verify: true - # udp: true - # ip-version: ipv6 - - # http - - name: "http" - type: http - server: server - port: 443 - # username: username - # password: password - # tls: true # https - # skip-cert-verify: true - # sni: custom.com - # fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints - # ip-version: dual - - # Snell - # Beware that there's currently no UDP support yet - - name: "snell" - type: snell - server: server - port: 44046 - psk: yourpsk - # version: 2 - # obfs-opts: - # mode: http # or tls - # host: bing.com - - # Trojan - - name: "trojan" - type: trojan - server: server - port: 443 - password: yourpsk - # client-fingerprint: random # Available: "chrome","firefox","safari","random","none" - # fingerprint: xxxx - # udp: true - # sni: example.com # aka server name - # alpn: - # - h2 - # - http/1.1 - # skip-cert-verify: true - - - name: trojan-grpc - server: server - port: 443 - type: trojan - password: "example" - network: grpc - sni: example.com - # skip-cert-verify: true - # fingerprint: xxxx - udp: true - grpc-opts: - grpc-service-name: "example" - - - name: trojan-ws - server: server - port: 443 - type: trojan - password: "example" - network: ws - sni: example.com - # skip-cert-verify: true - # fingerprint: xxxx - udp: true - # ws-opts: - # path: /path - # headers: - # Host: example.com - - - name: "trojan-xtls" - type: trojan - server: server - port: 443 - password: yourpsk - flow: "xtls-rprx-direct" # xtls-rprx-origin xtls-rprx-direct - flow-show: true - # udp: true - # sni: example.com # aka server name - # skip-cert-verify: true - # fingerprint: xxxx - + # vless - name: "vless-tcp" type: vless @@ -490,7 +421,21 @@ proxies: # skip-cert-verify: true # fingerprint: xxxx # client-fingerprint: random # Available: "chrome","firefox","safari","random","none" - + + - name: "vless-vision" + type: vless + server: server + port: 443 + uuid: uuid + network: tcp + tls: true + udp: true + xudp: true + flow: xtls-rprx-vision # xtls-rprx-origin # enable XTLS + client-fingerprint: chrome + # fingerprint: xxxx + # skip-cert-verify: true + - name: "vless-ws" type: vless server: server @@ -507,6 +452,61 @@ proxies: path: "/" headers: Host: example.com + + # Trojan + - name: "trojan" + type: trojan + server: server + port: 443 + password: yourpsk + # client-fingerprint: random # Available: "chrome","firefox","safari","random","none" + # fingerprint: xxxx + # udp: true + # sni: example.com # aka server name + # alpn: + # - h2 + # - http/1.1 + # skip-cert-verify: true + + - name: trojan-grpc + server: server + port: 443 + type: trojan + password: "example" + network: grpc + sni: example.com + # skip-cert-verify: true + # fingerprint: xxxx + udp: true + grpc-opts: + grpc-service-name: "example" + + - name: trojan-ws + server: server + port: 443 + type: trojan + password: "example" + network: ws + sni: example.com + # skip-cert-verify: true + # fingerprint: xxxx + udp: true + # ws-opts: + # path: /path + # headers: + # Host: example.com + + - name: "trojan-xtls" + type: trojan + server: server + port: 443 + password: yourpsk + flow: "xtls-rprx-direct" # xtls-rprx-origin xtls-rprx-direct + flow-show: true + # udp: true + # sni: example.com # aka server name + # skip-cert-verify: true + # fingerprint: xxxx #hysteria - name: "hysteria" @@ -533,7 +533,8 @@ proxies: # disable_mtu_discovery: false # fingerprint: xxxx # fast-open: true # 支持 TCP 快速打开,默认为 false - + + # wireguard - name: "wg" type: wireguard server: 162.159.192.1 @@ -543,7 +544,9 @@ proxies: private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU= public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= udp: true - # reserved: 'U4An' + reserved: "U4An" + + # tuic - name: tuic server: www.example.com port: 10443 @@ -552,16 +555,16 @@ proxies: # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' # heartbeat-interval: 10000 # alpn: [h3] - # disable-sni: true + disable-sni: true reduce-rtt: true - # request-timeout: 8000 + request-timeout: 8000 udp-relay-mode: native # Available: "native", "quic". Default: "native" # congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" # max-udp-relay-packet-size: 1500 # fast-open: true # skip-cert-verify: true # max-open-streams: 20 # default 100, too many open streams may hurt performance - + # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss # The supported obfses: @@ -582,8 +585,7 @@ proxies: # protocol-param: "#" # udp: true -proxy-groups: - # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP +proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP # Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet - name: "relay" type: relay @@ -592,7 +594,7 @@ proxy-groups: - vmess - ss1 - ss2 - + # url-test 将按照 url 测试结果使用延迟最低节点 - name: "auto" type: url-test @@ -604,7 +606,7 @@ proxy-groups: # lazy: true url: "https://cp.cloudflare.com/generate_204" interval: 300 - + # fallback 将按照 url 测试结果按照节点顺序选择 - name: "fallback-auto" type: fallback @@ -614,7 +616,7 @@ proxy-groups: - vmess1 url: "https://cp.cloudflare.com/generate_204" interval: 300 - + # load-balance 将按照算法随机选择节点 - name: "load-balance" type: load-balance @@ -624,8 +626,8 @@ proxy-groups: - vmess1 url: "https://cp.cloudflare.com/generate_204" interval: 300 - # strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions - + # strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions + # select 用户自行选择节点 - name: Proxy type: select @@ -635,7 +637,7 @@ proxy-groups: - ss2 - vmess1 - auto - + # 配置指定 interface-name 和 fwmark 的 DIRECT - name: en1 type: select @@ -643,7 +645,7 @@ proxy-groups: routing-mark: 6667 proxies: - DIRECT - + - name: UseProvider type: select filter: "HK|TW" # 正则表达式,过滤 provider1 中节点名包含 HK 或 TW @@ -690,7 +692,8 @@ rules: - DOMAIN-KEYWORD,google,ss1 - IP-CIDR,1.1.1.1/32,ss1 - IP-CIDR6,2409::/64,DIRECT - - SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1 # 当满足条件是 TCP 或 UDP 流量时,使用名为 sub-rule-name1 当规则集 + # 当满足条件是 TCP 或 UDP 流量时,使用名为 sub-rule-name1 的规则集 + - SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1 - SUB-RULE,(AND,((NETWORK,UDP))),sub-rule-name2 # 定义多个子规则集,规则将以分叉匹配,使用 SUB-RULE 使用 # google.com(not match)--> baidu.com(match) @@ -717,15 +720,6 @@ sub-rules: - IP-CIDR,8.8.8.8/32,ss1 - DOMAIN,dns.alidns.com,REJECT -tls: - certificate: string # 证书 PEM 格式,或者 证书的路径 - private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 - # 自定义证书验证,将加入 Clash 证书验证中,绝大多数 TLS 相关支持,如:DNS - # 可用于自定义证书的验证 - custom-certificates: - - certificate: string # 证书 PEM 格式,或者 证书的路径 - private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 - # 流量入站 listeners: - name: socks5-in-1 @@ -735,14 +729,14 @@ listeners: # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理 # udp: false # 默认 true - + - name: http-in-1 type: http port: 10809 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - + - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 port: 10810 @@ -750,14 +744,14 @@ listeners: # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - + - name: reidr-in-1 type: redir port: 10811 listen: 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - + - name: tproxy-in-1 type: tproxy port: 10812 @@ -765,7 +759,7 @@ listeners: # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - + - name: shadowsocks-in-1 type: shadowsocks port: 10813 @@ -774,7 +768,7 @@ listeners: # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg= cipher: 2022-blake3-aes-256-gcm - + - name: vmess-in-1 type: vmess port: 10814 @@ -785,7 +779,7 @@ listeners: - username: 1 uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68 alterId: 1 - + - name: tuic-in-1 type: tuic port: 10815 @@ -802,7 +796,7 @@ listeners: # alpn: # - h3 # max-udp-relay-packet-size: 1500 - + - name: tunnel-in-1 type: tunnel port: 10816 @@ -811,7 +805,7 @@ listeners: # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) network: [tcp, udp] target: target.com - + - name: tun-in-1 type: tun # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules @@ -827,25 +821,25 @@ listeners: inet6-address: # 必须手动设置ipv6地址段 - "fdfe:dcba:9877::1/126" # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 - # inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 - # - 0.0.0.0/1 - # - 128.0.0.0/1 - # inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 - # - "::/1" - # - "8000::/1" + # inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + # - 0.0.0.0/1 + # - 128.0.0.0/1 + # inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + # - "::/1" + # - "8000::/1" # endpoint_independent_nat: false # 启用独立于端点的 NAT # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route # - 0 # include_uid_range: # 限制被路由的的用户范围 # - 1000-99999 # exclude_uid: # 排除路由的的用户 - #- 1000 + # - 1000 # exclude_uid_range: # 排除路由的的用户范围 # - 1000-99999 - + # Android 用户和应用规则仅在 Android 下被支持 # 并且需要 auto_route - + # include_android_user: # 限制被路由的 Android 用户 # - 0 # - 10 @@ -853,3 +847,23 @@ listeners: # - com.android.chrome # exclude_package: # 排除被路由的 Android 应用包名 # - com.android.captiveportallogin + +# 入口配置与 Listener 等价,传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理 +# shadowsocks,vmess 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 +# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 + +# tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# tuic-server: +# enable: true +# listen: 127.0.0.1:10443 +# token: +# - TOKEN +# certificate: ./server.crt +# private-key: ./server.key +# congestion-controller: bbr +# max-idle-time: 15000 +# authentication-timeout: 1000 +# alpn: +# - h3 +# max-udp-relay-packet-size: 1500 \ No newline at end of file From de92bc0234a6b0a2b36f6580361e2d768d3a8805 Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Sat, 25 Feb 2023 19:11:23 +0800 Subject: [PATCH 022/149] fix: Vision filter Client Hello --- transport/vless/conn.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index fd1767cf..4750276e 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -215,12 +215,19 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { return vc.err } } - if vc.writeFilterApplicationData && vc.isTLS { + if vc.writeFilterApplicationData { buffer2 := ReshapeBuffer(buffer) defer buffer2.Release() vc.FilterTLS(buffer.Bytes()) command := commandPaddingContinue - if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 { + if !vc.isTLS { + command = commandPaddingEnd + + // disable XTLS + vc.readProcess = false + vc.writeFilterApplicationData = false + vc.packetsToFilter = 0 + } else if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 { command = commandPaddingEnd if vc.enableXTLS { command = commandPaddingDirect @@ -239,7 +246,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { //time.Sleep(10 * time.Millisecond) } if buffer2 != nil { - if vc.writeDirect { + if vc.writeDirect || !vc.isTLS { return vc.ExtendedWriter.WriteBuffer(buffer2) } vc.FilterTLS(buffer2.Bytes()) @@ -340,12 +347,7 @@ func (vc *Conn) sendRequest(p []byte) bool { if isVision && !vc.dst.UDP && !vc.dst.Mux { if len(p) == 0 { - WriteWithPadding(buffer, nil, commandPaddingEnd, vc.id) - - // disable XTLS - vc.readProcess = false - vc.writeFilterApplicationData = false - vc.packetsToFilter = 0 + WriteWithPadding(buffer, nil, commandPaddingContinue, vc.id) } else { vc.FilterTLS(p) if vc.isTLS { From a3b8c9c2339a03f5523a13c333cc0cb07a63de44 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 25 Feb 2023 19:41:01 +0800 Subject: [PATCH 023/149] fix: peek not work with some inbound --- common/net/bufconn.go | 4 ++++ tunnel/statistic/tracker.go | 12 ++++++------ tunnel/tunnel.go | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/common/net/bufconn.go b/common/net/bufconn.go index 54326cf9..8087608c 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -27,6 +27,10 @@ func (c *BufferedConn) Reader() *bufio.Reader { return c.r } +func (c *BufferedConn) ResetPeeked() { + c.peeked = false +} + func (c *BufferedConn) Peeked() bool { return c.peeked } diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 97dd7316..e21868c5 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -81,7 +81,7 @@ func (tt *tcpTracker) Upstream() any { return tt.Conn } -func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *tcpTracker { +func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *tcpTracker { uuid, _ := uuid.NewV4() if conn != nil { if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { @@ -100,8 +100,8 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R Metadata: metadata, Chain: conn.Chains(), Rule: "", - UploadTotal: atomic.NewInt64(0), - DownloadTotal: atomic.NewInt64(0), + UploadTotal: atomic.NewInt64(uploadTotal), + DownloadTotal: atomic.NewInt64(downloadTotal), }, extendedReader: N.NewExtendedReader(conn), extendedWriter: N.NewExtendedWriter(conn), @@ -147,7 +147,7 @@ func (ut *udpTracker) Close() error { return ut.PacketConn.Close() } -func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule) *udpTracker { +func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *udpTracker { uuid, _ := uuid.NewV4() metadata.RemoteDst = conn.RemoteDestination() @@ -160,8 +160,8 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru Metadata: metadata, Chain: conn.Chains(), Rule: "", - UploadTotal: atomic.NewInt64(0), - DownloadTotal: atomic.NewInt64(0), + UploadTotal: atomic.NewInt64(uploadTotal), + DownloadTotal: atomic.NewInt64(downloadTotal), }, } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b9d0e594..90fd42be 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -322,7 +322,7 @@ func handleUDPConn(packet C.PacketAdapter) { } pCtx.InjectPacketConn(rawPc) - pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule) + pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0) switch true { case metadata.SpecialProxy != "": @@ -367,6 +367,7 @@ func handleTCPConn(connCtx C.ConnContext) { } conn := connCtx.Conn() + conn.ResetPeeked() if sniffer.Dispatcher.Enable() && sniffingEnable { sniffer.Dispatcher.TCPSniff(conn, metadata) } @@ -400,6 +401,7 @@ func handleTCPConn(connCtx C.ConnContext) { } var peekBytes []byte + var peekLen int ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) defer cancel() @@ -415,7 +417,7 @@ func handleTCPConn(connCtx C.ConnContext) { if err != nil { return nil, err } - if peekLen := len(peekBytes); peekLen > 0 { + if peekLen = len(peekBytes); peekLen > 0 { _, _ = conn.Discard(peekLen) } return remoteConn, err @@ -436,7 +438,7 @@ func handleTCPConn(connCtx C.ConnContext) { return } - remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) + remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen)) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) From f565edd76dcd2f9db89c524310d1031052e96cbf Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 25 Feb 2023 22:01:20 +0800 Subject: [PATCH 024/149] chore: add custom ca trust --- component/tls/config.go | 48 +++++++++++++--------------------------- config/config.go | 11 ++++----- docs/config.yaml | 5 +++++ hub/executor/executor.go | 6 +++-- 4 files changed, 28 insertions(+), 42 deletions(-) diff --git a/component/tls/config.go b/component/tls/config.go index 39d1b1fd..50daad46 100644 --- a/component/tls/config.go +++ b/component/tls/config.go @@ -11,31 +11,30 @@ import ( "strings" "sync" - CN "github.com/Dreamacro/clash/common/net" - xtls "github.com/xtls/go" ) -var tlsCertificates = make([]tls.Certificate, 0) +var trustCert,_ = x509.SystemCertPool() var mutex sync.RWMutex var errNotMacth error = errors.New("certificate fingerprints do not match") -func AddCertificate(privateKey, certificate string) error { +func AddCertificate(certificate string) error { mutex.Lock() defer mutex.Unlock() - if cert, err := CN.ParseCert(certificate, privateKey); err != nil { - return err - } else { - tlsCertificates = append(tlsCertificates, cert) + if certificate == "" { + return fmt.Errorf("certificate is empty") + } + if ok := trustCert.AppendCertsFromPEM([]byte(certificate)); !ok { + return fmt.Errorf("add certificate failed") } return nil } -func GetCertificates() []tls.Certificate { - mutex.RLock() - defer mutex.RUnlock() - return tlsCertificates +func ResetCertificate(){ + mutex.Lock() + defer mutex.Unlock() + trustCert,_=x509.SystemCertPool() } func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { @@ -87,10 +86,10 @@ func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config { if tlsConfig == nil { return &tls.Config{ - Certificates: tlsCertificates, + RootCAs: trustCert, } } - tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCertificates...) + tlsConfig.RootCAs = trustCert return tlsConfig } @@ -107,29 +106,12 @@ func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint strin } func GetGlobalXTLSConfig(tlsConfig *xtls.Config) *xtls.Config { - xtlsCerts := make([]xtls.Certificate, len(tlsCertificates)) - for _, cert := range tlsCertificates { - tlsSsaList := make([]xtls.SignatureScheme, len(cert.SupportedSignatureAlgorithms)) - for _, ssa := range cert.SupportedSignatureAlgorithms { - tlsSsa := xtls.SignatureScheme(ssa) - tlsSsaList = append(tlsSsaList, tlsSsa) - } - xtlsCert := xtls.Certificate{ - Certificate: cert.Certificate, - PrivateKey: cert.PrivateKey, - OCSPStaple: cert.OCSPStaple, - SignedCertificateTimestamps: cert.SignedCertificateTimestamps, - Leaf: cert.Leaf, - SupportedSignatureAlgorithms: tlsSsaList, - } - xtlsCerts = append(xtlsCerts, xtlsCert) - } if tlsConfig == nil { return &xtls.Config{ - Certificates: xtlsCerts, + RootCAs: trustCert, } } - tlsConfig.Certificates = xtlsCerts + tlsConfig.RootCAs = trustCert return tlsConfig } diff --git a/config/config.go b/config/config.go index 24159d8e..76e5491b 100644 --- a/config/config.go +++ b/config/config.go @@ -120,13 +120,9 @@ type Profile struct { } type TLS struct { - RawCert `yaml:",inline"` - CustomTrustCert []RawCert `yaml:"custom-certifactes"` -} - -type RawCert struct { - Certificate string `yaml:"certificate"` - PrivateKey string `yaml:"private-key"` + Certificate string `yaml:"certificate"` + PrivateKey string `yaml:"private-key"` + CustomTrustCert []string `yaml:"custom-certifactes"` } // IPTables config @@ -447,6 +443,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.General = general + dialer.DefaultInterface.Store(config.General.Interface) proxies, providers, err := parseProxies(rawCfg) if err != nil { return nil, err diff --git a/docs/config.yaml b/docs/config.yaml index f6e9502e..771532d1 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -30,6 +30,11 @@ ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS tls: certificate: string # 证书 PEM 格式,或者 证书的路径 private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 + custom-certifactes: + - | + -----BEGIN CERTIFICATE----- + format/pem... + -----END CERTIFICATE----- external-controller: 0.0.0.0:9093 # RESTful API 监听地址 external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 916f17c7..34f0f1a1 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -169,9 +169,11 @@ func updateExperimental(c *config.Config) { } func preUpdateExperimental(c *config.Config) { - CTLS.AddCertificate(c.TLS.PrivateKey, c.TLS.Certificate) + CTLS.ResetCertificate() for _, c := range c.TLS.CustomTrustCert { - CTLS.AddCertificate(c.PrivateKey, c.Certificate) + if err := CTLS.AddCertificate(c); err != nil { + log.Warnln("%s\nadd error: %s", c, err.Error()) + } } } From efbde4a179342b14bf8811f4ea42dbf77cca1988 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 11:11:54 +0800 Subject: [PATCH 025/149] fix: reject's dial warning --- adapter/outbound/reject.go | 3 --- tunnel/tunnel.go | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index d5a9c823..43833238 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -53,9 +53,6 @@ func (rw *nopConn) Read(b []byte) (int, error) { } func (rw *nopConn) Write(b []byte) (int, error) { - if len(b) == 0 { - return 0, nil - } return 0, io.EOF } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 90fd42be..11458d38 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -410,6 +410,11 @@ func handleTCPConn(connCtx C.ConnContext) { if err != nil { return nil, err } + for _, chain := range remoteConn.Chains() { + if chain == "REJECT" { + return remoteConn, nil + } + } peekMutex.Lock() defer peekMutex.Unlock() peekBytes, _ = conn.Peek(conn.Buffered()) From 40ae019e1dcad6bd46177be49f39c1e0c5074f3c Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Sun, 26 Feb 2023 11:11:55 +0800 Subject: [PATCH 026/149] fix: Vision filter TLS 1.2 --- transport/vless/conn.go | 15 ++++++++++----- transport/vless/filter.go | 13 ++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 4750276e..5817083d 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -133,15 +133,16 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { vc.readProcess = false return vc.ReadBuffer(buffer) case commandPaddingDirect: + needReturn := false if vc.input != nil { _, err := buffer.ReadFrom(vc.input) if err != nil { return err } if vc.input.Len() == 0 { + needReturn = true vc.input = nil - } - if buffer.IsFull() { + } else { // buffer is full return nil } } @@ -150,6 +151,7 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { if err != nil { return err } + needReturn = true if vc.rawInput.Len() == 0 { vc.rawInput = nil } @@ -159,6 +161,9 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { vc.ExtendedReader = N.NewExtendedReader(vc.Conn) log.Debugln("XTLS Vision direct read start") } + if needReturn { + return nil + } default: err := fmt.Errorf("XTLS Vision read unknown command: %d", vc.readLastCommand) log.Debugln(err.Error()) @@ -489,9 +494,9 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { r, _ := t.FieldByName("rawInput") c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) - if _, ok := c.Conn.(*net.TCPConn); !ok { - log.Debugln("XTLS underlying conn is not *net.TCPConn, got %s", reflect.TypeOf(conn).Name()) - } + // if _, ok := c.Conn.(*net.TCPConn); !ok { + // log.Debugln("XTLS underlying conn is not *net.TCPConn, got %T", c.Conn) + // } } } diff --git a/transport/vless/filter.go b/transport/vless/filter.go index 15d595bc..5e00b5c3 100644 --- a/transport/vless/filter.go +++ b/transport/vless/filter.go @@ -50,10 +50,13 @@ func (vc *Conn) FilterTLS(p []byte) (index int) { } if vc.remainingServerHello > 0 { - end := vc.remainingServerHello - vc.remainingServerHello -= end - if end > uint16(lenP) { - end = uint16(lenP) + end := int(vc.remainingServerHello) + if index+end > lenP { + end = lenP + vc.remainingServerHello -= uint16(end - index) + } else { + vc.remainingServerHello -= uint16(end) + end += index } if bytes.Contains(p[index:end], tls13SupportedVersions) { // TLS 1.3 Client Hello @@ -64,7 +67,7 @@ func (vc *Conn) FilterTLS(p []byte) (index int) { log.Debugln("XTLS Vision found TLS 1.3, packetLength=", lenP, ", CipherSuite=", cs) vc.packetsToFilter = 0 return - } else if vc.remainingServerHello < 0 { + } else if vc.remainingServerHello <= 0 { log.Debugln("XTLS Vision found TLS 1.2, packetLength=", lenP) vc.packetsToFilter = 0 return From 5e7d644efd0dcad4fe5b826c7ca0051c572b9746 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 11:18:01 +0800 Subject: [PATCH 027/149] fix: ensure peekMutex is locked before handleSocket --- tunnel/tunnel.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 11458d38..c52400e8 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -469,6 +469,8 @@ func handleTCPConn(connCtx C.ConnContext) { ) } + peekMutex.Lock() + defer peekMutex.Unlock() handleSocket(connCtx, remoteConn) } From 97e14337e355a289b21227b8dcc9ba1d6d7e44e4 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Feb 2023 11:24:49 +0800 Subject: [PATCH 028/149] refactor: tcp dial (#412) Non-concurrent support to try to connect in turn fix: serial dual stack dial --- component/dialer/dialer.go | 413 +++++++++++++++---------------------- hub/executor/executor.go | 6 +- 2 files changed, 170 insertions(+), 249 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 9ac9d719..ab2fe047 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -8,20 +8,19 @@ import ( "net/netip" "strings" "sync" + "time" "github.com/Dreamacro/clash/component/resolver" - - "go.uber.org/atomic" ) var ( - dialMux sync.Mutex - actualSingleDialContext = singleDialContext - actualDualStackDialContext = dualStackDialContext - tcpConcurrent = false - DisableIPv6 = false - ErrorInvalidedNetworkStack = errors.New("invalided network stack") - ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel") + dialMux sync.Mutex + actualSingleStackDialContext = serialSingleStackDialContext + actualDualStackDialContext = serialDualStackDialContext + tcpConcurrent = false + ErrorInvalidedNetworkStack = errors.New("invalided network stack") + ErrorConnTimeout = errors.New("connect timeout") + fallbackTimeout = 300 * time.Millisecond ) func applyOptions(options ...Option) *option { @@ -56,7 +55,7 @@ func DialContext(ctx context.Context, network, address string, options ...Option switch network { case "tcp4", "tcp6", "udp4", "udp6": - return actualSingleDialContext(ctx, network, address, opt) + return actualSingleStackDialContext(ctx, network, address, opt) case "tcp", "udp": return actualDualStackDialContext(ctx, network, address, opt) default: @@ -89,11 +88,11 @@ func SetDial(concurrent bool) { dialMux.Lock() tcpConcurrent = concurrent if concurrent { - actualSingleDialContext = concurrentSingleDialContext + actualSingleStackDialContext = concurrentSingleStackDialContext actualDualStackDialContext = concurrentDualStackDialContext } else { - actualSingleDialContext = singleDialContext - actualDualStackDialContext = dualStackDialContext + actualSingleStackDialContext = serialSingleStackDialContext + actualDualStackDialContext = serialDualStackDialContext } dialMux.Unlock() @@ -114,10 +113,6 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po bindMarkToDialer(opt.routingMark, dialer, network, destination) } - if DisableIPv6 && destination.Is6() { - return nil, ErrorDisableIPv6 - } - address := net.JoinHostPort(destination.String(), port) if opt.tfo { return dialTFO(ctx, *dialer, network, address) @@ -125,146 +120,74 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return dialer.DialContext(ctx, network, address) } -func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) +func serialSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { + ips, port, err := parseAddr(ctx, network, address, opt.resolver) if err != nil { return nil, err } - - var ip netip.Addr - switch network { - case "tcp4", "udp4": - if opt.resolver == nil { - ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) - } else { - ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver) - } - default: - if opt.resolver == nil { - ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) - } else { - ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver) - } - } - if err != nil { - err = fmt.Errorf("dns resolve failed:%w", err) - return nil, err - } - - return dialContext(ctx, network, ip, port, opt) + return serialDialContext(ctx, network, ips, port, opt) } -func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) +func serialDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { + ips, port, err := parseAddr(ctx, network, address, opt.resolver) + if err != nil { + return nil, err + } + if opt.prefer != 4 && opt.prefer != 6 { + return serialDialContext(ctx, network, ips, port, opt) + } + return dualStackDialContext( + ctx, + func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) }, + func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) }, + opt.prefer == 4) +} + +func concurrentSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { + ips, port, err := parseAddr(ctx, network, address, opt.resolver) if err != nil { return nil, err } - returned := make(chan struct{}) - defer close(returned) - - type dialResult struct { - net.Conn - error - resolved bool - ipv6 bool - done bool - } - results := make(chan dialResult) - var primary, fallback dialResult - - startRacer := func(ctx context.Context, network, host string, r resolver.Resolver, ipv6 bool) { - result := dialResult{ipv6: ipv6, done: true} - defer func() { - select { - case results <- result: - case <-returned: - if result.Conn != nil { - _ = result.Conn.Close() - } - } - }() - - var ip netip.Addr - if ipv6 { - if r == nil { - ip, result.error = resolver.ResolveIPv6ProxyServerHost(ctx, host) - } else { - ip, result.error = resolver.ResolveIPv6WithResolver(ctx, host, r) - } - } else { - if r == nil { - ip, result.error = resolver.ResolveIPv4ProxyServerHost(ctx, host) - } else { - ip, result.error = resolver.ResolveIPv4WithResolver(ctx, host, r) - } - } - if result.error != nil { - result.error = fmt.Errorf("dns resolve failed:%w", result.error) - return - } - result.resolved = true - - result.Conn, result.error = dialContext(ctx, network, ip, port, opt) - } - - go startRacer(ctx, network+"4", host, opt.resolver, false) - go startRacer(ctx, network+"6", host, opt.resolver, true) - - count := 2 - for i := 0; i < count; i++ { - select { - case res := <-results: - if res.error == nil { - return res.Conn, nil - } - - if !res.ipv6 { - primary = res - } else { - fallback = res - } - - if primary.done && fallback.done { - if primary.resolved { - return nil, primary.error - } else if fallback.resolved { - return nil, fallback.error - } else { - return nil, primary.error - } - } - case <-ctx.Done(): - err = ctx.Err() - break - } - } - - if err == nil { - err = fmt.Errorf("dual stack dial failed") + if conn, err := parallelDialContext(ctx, network, ips, port, opt); err != nil { + return nil, err } else { - err = fmt.Errorf("dual stack dial failed:%w", err) + return conn, nil } - return nil, err } -func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { +func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { + ips, port, err := parseAddr(ctx, network, address, opt.resolver) + if err != nil { + return nil, err + } + if opt.prefer != 4 && opt.prefer != 6 { + return parallelDialContext(ctx, network, ips, port, opt) + } + ipv4s, ipv6s := sortationAddr(ips) + return dualStackDialContext( + ctx, + func(ctx context.Context) (net.Conn, error) { + return parallelDialContext(ctx, network, ipv4s, port, opt) + }, + func(ctx context.Context) (net.Conn, error) { + return parallelDialContext(ctx, network, ipv6s, port, opt) + }, + opt.prefer == 4) +} + +func dualStackDialContext( + ctx context.Context, + ipv4DialFn func(ctx context.Context) (net.Conn, error), + ipv6DialFn func(ctx context.Context) (net.Conn, error), + preferIPv4 bool) (net.Conn, error) { + fallbackTicker := time.NewTicker(fallbackTimeout) + defer fallbackTicker.Stop() + results := make(chan dialResult) returned := make(chan struct{}) defer close(returned) - - type dialResult struct { - ip netip.Addr - net.Conn - error - isPrimary bool - done bool - } - - preferCount := atomic.NewInt32(0) - results := make(chan dialResult) - tcpRacer := func(ctx context.Context, ip netip.Addr) { - result := dialResult{ip: ip, done: true} - + racer := func(dial func(ctx context.Context) (net.Conn, error), isPrimary bool) { + result := dialResult{isPrimary: isPrimary} defer func() { select { case results <- result: @@ -274,140 +197,142 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr } } }() - if strings.Contains(network, "tcp") { - network = "tcp" - } else { - network = "udp" - } - - if ip.Is6() { - network += "6" - if opt.prefer != 4 { - result.isPrimary = true - } - } - - if ip.Is4() { - network += "4" - if opt.prefer != 6 { - result.isPrimary = true - } - } - - if result.isPrimary { - preferCount.Add(1) - } - - result.Conn, result.error = dialContext(ctx, network, ip, port, opt) + result.Conn, result.error = dial(ctx) } - - for _, ip := range ips { - go tcpRacer(ctx, ip) - } - - connCount := len(ips) + go racer(ipv4DialFn, preferIPv4) + go racer(ipv6DialFn, !preferIPv4) var fallback dialResult - var primaryError error - var finalError error - for i := 0; i < connCount; i++ { + var err error + for { select { + case <-ctx.Done(): + if fallback.error == nil && fallback.Conn != nil { + return fallback.Conn, nil + } + return nil, fmt.Errorf("dual stack connect failed: %w", err) + case <-fallbackTicker.C: + if fallback.error == nil && fallback.Conn != nil { + return fallback.Conn, nil + } case res := <-results: if res.error == nil { if res.isPrimary { return res.Conn, nil - } else { - if !fallback.done || fallback.error != nil { - fallback = res - } - } - } else { - if res.isPrimary { - primaryError = res.error - preferCount.Add(-1) - if preferCount.Load() == 0 && fallback.done && fallback.error == nil { - return fallback.Conn, nil - } } + fallback = res } - case <-ctx.Done(): - if fallback.done && fallback.error == nil { - return fallback.Conn, nil - } - finalError = ctx.Err() - break + err = res.error } } - - if fallback.done && fallback.error == nil { - return fallback.Conn, nil - } - - if primaryError != nil { - return nil, primaryError - } - - if fallback.error != nil { - return nil, fallback.error - } - - if finalError == nil { - finalError = fmt.Errorf("all ips %v tcp shake hands failed", ips) - } else { - finalError = fmt.Errorf("concurrent dial failed:%w", finalError) - } - - return nil, finalError } -func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { +func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + results := make(chan dialResult) + returned := make(chan struct{}) + defer close(returned) + tcpRacer := func(ctx context.Context, ip netip.Addr, port string) { + result := dialResult{isPrimary: true} + defer func() { + select { + case results <- result: + case <-returned: + if result.Conn != nil { + _ = result.Conn.Close() + } + } + }() + result.ip = ip + result.Conn, result.error = dialContext(ctx, network, ip, port, opt) + } + + for _, ip := range ips { + go tcpRacer(ctx, ip, port) + } + var err error + for { + select { + case <-ctx.Done(): + if err != nil { + return nil, err + } + if ctx.Err() == context.DeadlineExceeded { + return nil, ErrorConnTimeout + } + return nil, ctx.Err() + case res := <-results: + if res.error == nil { + return res.Conn, nil + } + err = res.error + } + } +} + +func serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + var ( + conn net.Conn + err error + errs []error + ) + for _, ip := range ips { + if conn, err = dialContext(ctx, network, ip, port, opt); err == nil { + return conn, nil + } else { + errs = append(errs, err) + } + } + return nil, errors.Join(errs...) +} + +type dialResult struct { + ip netip.Addr + net.Conn + error + isPrimary bool +} + +func parseAddr(ctx context.Context, network, address string, preferResolver resolver.Resolver) ([]netip.Addr, string, error) { host, port, err := net.SplitHostPort(address) if err != nil { - return nil, err + return nil, "-1", err } var ips []netip.Addr switch network { case "tcp4", "udp4": - if opt.resolver == nil { + if preferResolver == nil { ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) } else { - ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver) + ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) } - default: - if opt.resolver == nil { + case "tcp6", "udp6": + if preferResolver == nil { ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) } else { - ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver) + ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) + } + default: + if preferResolver == nil { + ips, err = resolver.LookupIP(ctx, host) + } else { + ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) } } - if err != nil { - err = fmt.Errorf("dns resolve failed:%w", err) - return nil, err + return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) } - - return concurrentDialContext(ctx, network, ips, port, opt) + return ips, port, nil } -func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, err +func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { + for _, v := range ips { + if v.Is4() || v.Is4In6() { + ipv4s = append(ipv4s, v) + } else { + ipv6s = append(ipv6s, v) + } } - - var ips []netip.Addr - if opt.resolver != nil { - ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver) - } else { - ips, err = resolver.LookupIPProxyServerHost(ctx, host) - } - - if err != nil { - err = fmt.Errorf("dns resolve failed:%w", err) - return nil, err - } - - return concurrentDialContext(ctx, network, ips, port, opt) + return } type Dialer struct { diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 34f0f1a1..c55201cd 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -331,11 +331,7 @@ func updateTunnels(tunnels []LC.Tunnel) { func updateGeneral(general *config.General) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) - dialer.DisableIPv6 = !general.IPv6 - if !dialer.DisableIPv6 { - log.Infoln("Use IPv6") - } - resolver.DisableIPv6 = dialer.DisableIPv6 + resolver.DisableIPv6 =!general.IPv6 if general.TCPConcurrent { dialer.SetDial(general.TCPConcurrent) From 0a6705f43e1d47a6a5c0a8bc21542c6541ad4ead Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Feb 2023 12:39:53 +0800 Subject: [PATCH 029/149] fix: ip version prefer not working --- component/dialer/dialer.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index ab2fe047..1925e86c 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -136,10 +136,11 @@ func serialDualStackDialContext(ctx context.Context, network, address string, op if opt.prefer != 4 && opt.prefer != 6 { return serialDialContext(ctx, network, ips, port, opt) } + ipv4s, ipv6s := sortationAddr(ips) return dualStackDialContext( ctx, - func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) }, - func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) }, + func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv4s, port, opt) }, + func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv6s, port, opt) }, opt.prefer == 4) } From 2cbfac2c89b77b654f4468bf515cb3b115ed650c Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Sun, 26 Feb 2023 13:04:12 +0800 Subject: [PATCH 030/149] fix: Filter slice index out of bounds --- transport/vless/filter.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/transport/vless/filter.go b/transport/vless/filter.go index 5e00b5c3..099d2de1 100644 --- a/transport/vless/filter.go +++ b/transport/vless/filter.go @@ -51,14 +51,18 @@ func (vc *Conn) FilterTLS(p []byte) (index int) { if vc.remainingServerHello > 0 { end := int(vc.remainingServerHello) - if index+end > lenP { + i := index + if i < 0 { + i = 0 + } + if i+end > lenP { end = lenP - vc.remainingServerHello -= uint16(end - index) + vc.remainingServerHello -= uint16(end - i) } else { vc.remainingServerHello -= uint16(end) - end += index + end += i } - if bytes.Contains(p[index:end], tls13SupportedVersions) { + if bytes.Contains(p[i:end], tls13SupportedVersions) { // TLS 1.3 Client Hello cs, ok := tls13CipherSuiteMap[vc.cipher] if ok && cs != "TLS_AES_128_CCM_8_SHA256" { From 0321fe93cf459471ed585dc428fd77eed87da9f0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 13:05:55 +0800 Subject: [PATCH 031/149] fix: replace self define "connect timeout" to os.ErrDeadlineExceeded --- component/dialer/dialer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 1925e86c..b9c897d2 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/netip" + "os" "strings" "sync" "time" @@ -19,7 +20,6 @@ var ( actualDualStackDialContext = serialDualStackDialContext tcpConcurrent = false ErrorInvalidedNetworkStack = errors.New("invalided network stack") - ErrorConnTimeout = errors.New("connect timeout") fallbackTimeout = 300 * time.Millisecond ) @@ -257,7 +257,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, return nil, err } if ctx.Err() == context.DeadlineExceeded { - return nil, ErrorConnTimeout + return nil, os.ErrDeadlineExceeded } return nil, ctx.Err() case res := <-results: From be5ce6249f1dd84cccbc0b4d9296badf62821456 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 13:52:10 +0800 Subject: [PATCH 032/149] fix: dns resolve in dialer --- component/dialer/dialer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index b9c897d2..3d1be1d6 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -314,7 +314,7 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso } default: if preferResolver == nil { - ips, err = resolver.LookupIP(ctx, host) + ips, err = resolver.LookupIPProxyServerHost(ctx, host) } else { ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) } @@ -328,7 +328,7 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { for _, v := range ips { if v.Is4() || v.Is4In6() { - ipv4s = append(ipv4s, v) + ipv4s = append(ipv4s, v.Unmap()) } else { ipv6s = append(ipv6s, v) } From 3cd1c92162f83417cd1342268076ab7c6c90de01 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 15:59:34 +0800 Subject: [PATCH 033/149] fix: uot client's WriteTo mistake --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9c3b78ac..d6103297 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( 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.20230221060643-3401d210384b + github.com/sagernet/sing v0.1.8-0.20230226075703-7def9588a57c github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e github.com/sagernet/sing-vmess v0.1.2 github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d diff --git a/go.sum b/go.sum index c06b8506..8a5a42e8 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ 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.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo= -github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.1.8-0.20230226075703-7def9588a57c h1:PDkrM1NhN03w6AtmBxQldH/mmqNGbKhIi6uWdiTOf9g= +github.com/sagernet/sing v0.1.8-0.20230226075703-7def9588a57c/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= From e6a35199e0d397d68994d4b6a9e8312073596260 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Feb 2023 20:15:28 +0800 Subject: [PATCH 034/149] fix: dual stack serial dial --- component/dialer/dialer.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3d1be1d6..d4c937e7 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -133,15 +133,12 @@ func serialDualStackDialContext(ctx context.Context, network, address string, op if err != nil { return nil, err } - if opt.prefer != 4 && opt.prefer != 6 { - return serialDialContext(ctx, network, ips, port, opt) - } ipv4s, ipv6s := sortationAddr(ips) return dualStackDialContext( ctx, func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv4s, port, opt) }, func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv6s, port, opt) }, - opt.prefer == 4) + opt.prefer) } func concurrentSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { @@ -174,14 +171,14 @@ func concurrentDualStackDialContext(ctx context.Context, network, address string func(ctx context.Context) (net.Conn, error) { return parallelDialContext(ctx, network, ipv6s, port, opt) }, - opt.prefer == 4) + opt.prefer) } func dualStackDialContext( ctx context.Context, ipv4DialFn func(ctx context.Context) (net.Conn, error), ipv6DialFn func(ctx context.Context) (net.Conn, error), - preferIPv4 bool) (net.Conn, error) { + preferIPVersion int) (net.Conn, error) { fallbackTicker := time.NewTicker(fallbackTimeout) defer fallbackTicker.Stop() results := make(chan dialResult) @@ -200,8 +197,8 @@ func dualStackDialContext( }() result.Conn, result.error = dial(ctx) } - go racer(ipv4DialFn, preferIPv4) - go racer(ipv6DialFn, !preferIPv4) + go racer(ipv4DialFn, preferIPVersion != 6) + go racer(ipv6DialFn, preferIPVersion != 4) var fallback dialResult var err error for { From e1dd4ac9e79cabae47601004c84a652015f8c7fe Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Feb 2023 20:38:32 +0800 Subject: [PATCH 035/149] chore: format code --- component/tls/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/component/tls/config.go b/component/tls/config.go index 50daad46..f0155d78 100644 --- a/component/tls/config.go +++ b/component/tls/config.go @@ -14,7 +14,7 @@ import ( xtls "github.com/xtls/go" ) -var trustCert,_ = x509.SystemCertPool() +var trustCert, _ = x509.SystemCertPool() var mutex sync.RWMutex var errNotMacth error = errors.New("certificate fingerprints do not match") @@ -31,10 +31,10 @@ func AddCertificate(certificate string) error { return nil } -func ResetCertificate(){ +func ResetCertificate() { mutex.Lock() defer mutex.Unlock() - trustCert,_=x509.SystemCertPool() + trustCert, _ = x509.SystemCertPool() } func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { From d36f9c2ac811b41b8ded4e3bf34077608899594d Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 26 Feb 2023 21:01:44 +0800 Subject: [PATCH 036/149] fix: handle no IP address --- component/dialer/dialer.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index d4c937e7..3a37620d 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -218,13 +218,17 @@ func dualStackDialContext( return res.Conn, nil } fallback = res + } else { + err = res.error } - err = res.error } } } func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + if len(ips) == 0 { + return nil, errors.New("no ip address") + } results := make(chan dialResult) returned := make(chan struct{}) defer close(returned) @@ -267,6 +271,9 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, } func serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + if len(ips) == 0 { + return nil, errors.New("no ip address") + } var ( conn net.Conn err error From c8c078e78a97afa0f6bb2755573e5dff24c867f8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 26 Feb 2023 22:20:25 +0800 Subject: [PATCH 037/149] fix: golang1.19 can't compile --- component/dialer/dialer.go | 8 +++----- component/dialer/error.go | 18 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 component/dialer/error.go diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3a37620d..2d3937c8 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -2,7 +2,6 @@ package dialer import ( "context" - "errors" "fmt" "net" "net/netip" @@ -19,7 +18,6 @@ var ( actualSingleStackDialContext = serialSingleStackDialContext actualDualStackDialContext = serialDualStackDialContext tcpConcurrent = false - ErrorInvalidedNetworkStack = errors.New("invalided network stack") fallbackTimeout = 300 * time.Millisecond ) @@ -227,7 +225,7 @@ func dualStackDialContext( func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { if len(ips) == 0 { - return nil, errors.New("no ip address") + return nil, ErrorNoIpAddress } results := make(chan dialResult) returned := make(chan struct{}) @@ -272,7 +270,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, func serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { if len(ips) == 0 { - return nil, errors.New("no ip address") + return nil, ErrorNoIpAddress } var ( conn net.Conn @@ -286,7 +284,7 @@ func serialDialContext(ctx context.Context, network string, ips []netip.Addr, po errs = append(errs, err) } } - return nil, errors.Join(errs...) + return nil, errorsJoin(errs...) } type dialResult struct { diff --git a/component/dialer/error.go b/component/dialer/error.go new file mode 100644 index 00000000..f2f6b4b7 --- /dev/null +++ b/component/dialer/error.go @@ -0,0 +1,18 @@ +package dialer + +import ( + "errors" + + E "github.com/sagernet/sing/common/exceptions" +) + +var ( + ErrorNoIpAddress = errors.New("no ip address") + ErrorInvalidedNetworkStack = errors.New("invalided network stack") +) + +func errorsJoin(errs ...error) error { + // compatibility with golang<1.20 + // maybe use errors.Join(errs...) is better after we drop the old version's support + return E.Errors(errs...) +} diff --git a/go.mod b/go.mod index d6103297..401affac 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( 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.20230226075703-7def9588a57c + github.com/sagernet/sing v0.1.8-0.20230226133421-e83948367009 github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e github.com/sagernet/sing-vmess v0.1.2 github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d diff --git a/go.sum b/go.sum index 8a5a42e8..79cbc722 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ 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.20230226075703-7def9588a57c h1:PDkrM1NhN03w6AtmBxQldH/mmqNGbKhIi6uWdiTOf9g= -github.com/sagernet/sing v0.1.8-0.20230226075703-7def9588a57c/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +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-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= From c1199f1a8abc3088a8330cce50232e2ed6edff02 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 27 Feb 2023 00:26:49 +0800 Subject: [PATCH 038/149] 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) } From 0b56fc7598a35644693ee8c314aa74122421c1f2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 27 Feb 2023 01:06:41 +0800 Subject: [PATCH 039/149] fix: Vision filter TLS 1.2 Add magic from sing-box. https://github.com/SagerNet/sing-box/blob/5ce3ddee9be640c309a23341951434861dc4c2e0/transport/vless/vision.go#L199 --- transport/vless/conn.go | 22 ++++++++++++-------- transport/vless/filter.go | 43 ++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 4ee34405..82450cf1 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -248,7 +248,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { if vc.writeDirect { vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn) log.Debugln("XTLS Vision direct write start") - //time.Sleep(10 * time.Millisecond) + //time.Sleep(5 * time.Millisecond) } if buffer2 != nil { if vc.writeDirect || !vc.isTLS { @@ -393,22 +393,22 @@ func (vc *Conn) sendRequest(p []byte) bool { } func (vc *Conn) recvResponse() error { - var buf [1]byte - _, vc.err = io.ReadFull(vc.ExtendedReader, buf[:]) + var buffer [1]byte + _, vc.err = io.ReadFull(vc.ExtendedReader, buffer[:]) if vc.err != nil { return vc.err } - if buf[0] != Version { + if buffer[0] != Version { return errors.New("unexpected response version") } - _, vc.err = io.ReadFull(vc.ExtendedReader, buf[:]) + _, vc.err = io.ReadFull(vc.ExtendedReader, buffer[:]) if vc.err != nil { return vc.err } - length := int64(buf[0]) + length := int64(buffer[0]) if length != 0 { // addon data length > 0 io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard } @@ -482,19 +482,23 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { var p uintptr switch underlying := conn.(type) { case *gotls.Conn: + //log.Debugln("type tls") c.Conn = underlying.NetConn() c.tlsConn = underlying t = reflect.TypeOf(underlying).Elem() p = uintptr(unsafe.Pointer(underlying)) case *utls.UConn: + //log.Debugln("type *utls.UConn") c.Conn = underlying.NetConn() c.tlsConn = underlying t = reflect.TypeOf(underlying.Conn).Elem() p = uintptr(unsafe.Pointer(underlying.Conn)) case *tlsC.UConn: + //log.Debugln("type *tlsC.UConn") c.Conn = underlying.NetConn() c.tlsConn = underlying.UConn t = reflect.TypeOf(underlying.Conn).Elem() + //log.Debugln("t:%v", t) p = uintptr(unsafe.Pointer(underlying.Conn)) default: return nil, fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, client.Addons.Flow) @@ -503,9 +507,9 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { r, _ := t.FieldByName("rawInput") c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) - // if _, ok := c.Conn.(*net.TCPConn); !ok { - // log.Debugln("XTLS underlying conn is not *net.TCPConn, got %T", c.Conn) - // } + //if _, ok := c.Conn.(*net.TCPConn); !ok { + // log.Debugln("XTLS underlying conn is not *net.TCPConn, got %T", c.Conn) + //} } } diff --git a/transport/vless/filter.go b/transport/vless/filter.go index 099d2de1..e16100d5 100644 --- a/transport/vless/filter.go +++ b/transport/vless/filter.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/binary" - log "github.com/sirupsen/logrus" + "github.com/Dreamacro/clash/log" ) var ( @@ -27,24 +27,30 @@ const ( tlsHandshakeTypeServerHello byte = 0x02 ) -func (vc *Conn) FilterTLS(p []byte) (index int) { +func (vc *Conn) FilterTLS(buffer []byte) (index int) { if vc.packetsToFilter <= 0 { return 0 } - lenP := len(p) - vc.packetsToFilter -= 1 - if index = bytes.Index(p, tlsServerHandshakeStart); index != -1 { - if lenP >= index+5 && p[index+5] == tlsHandshakeTypeServerHello { - vc.remainingServerHello = binary.BigEndian.Uint16(p[index+3:]) + 5 - vc.isTLS = true - vc.isTLS12orAbove = true - if lenP-index >= 79 && vc.remainingServerHello >= 79 { - sessionIDLen := int(p[index+43]) - vc.cipher = binary.BigEndian.Uint16(p[index+43+sessionIDLen+1:]) + lenP := len(buffer) + vc.packetsToFilter-- + if index := bytes.Index(buffer, tlsServerHandshakeStart); index != -1 { + if lenP >= index+5 { + if buffer[0] == 22 && buffer[1] == 3 && buffer[2] == 3 { + vc.isTLS = true + if buffer[5] == tlsHandshakeTypeServerHello { + log.Debugln("isTLS12orAbove") + vc.remainingServerHello = binary.BigEndian.Uint16(buffer[index+3:]) + 5 + + vc.isTLS12orAbove = true + if lenP-index >= 79 && vc.remainingServerHello >= 79 { + sessionIDLen := int(buffer[index+43]) + vc.cipher = binary.BigEndian.Uint16(buffer[index+43+sessionIDLen+1:]) + } + } } } - } else if index = bytes.Index(p, tlsClientHandshakeStart); index != -1 { - if lenP >= index+5 && p[index+5] == tlsHandshakeTypeClientHello { + } else if index := bytes.Index(buffer, tlsClientHandshakeStart); index != -1 { + if lenP >= index+5 && buffer[index+5] == tlsHandshakeTypeClientHello { vc.isTLS = true } } @@ -62,22 +68,21 @@ func (vc *Conn) FilterTLS(p []byte) (index int) { vc.remainingServerHello -= uint16(end) end += i } - if bytes.Contains(p[i:end], tls13SupportedVersions) { + if bytes.Contains(buffer[i:end], tls13SupportedVersions) { // TLS 1.3 Client Hello cs, ok := tls13CipherSuiteMap[vc.cipher] if ok && cs != "TLS_AES_128_CCM_8_SHA256" { vc.enableXTLS = true } - log.Debugln("XTLS Vision found TLS 1.3, packetLength=", lenP, ", CipherSuite=", cs) + log.Debugln("XTLS Vision found TLS 1.3, packetLength= %d CipherSuite= %s", lenP, cs) vc.packetsToFilter = 0 return } else if vc.remainingServerHello <= 0 { - log.Debugln("XTLS Vision found TLS 1.2, packetLength=", lenP) + log.Debugln("XTLS Vision found TLS 1.2, packetLength= %d", lenP) vc.packetsToFilter = 0 return } - log.Debugln("XTLS Vision found inconclusive server hello, packetLength=", lenP, - ", remainingServerHelloBytes=", vc.remainingServerHello) + log.Debugln("XTLS Vision found inconclusive server hello, packetLength= %d,remainingServerHelloBytes= %d", lenP, vc.remainingServerHello) } if vc.packetsToFilter <= 0 { log.Debugln("XTLS Vision stop filtering") From 78100aa9634d529ea34acccab80be478fa784b6b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 27 Feb 2023 09:46:00 +0800 Subject: [PATCH 040/149] fix: vless NeedHandshake mistake --- transport/vless/conn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 82450cf1..53dc7c85 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -433,10 +433,10 @@ func (vc *Conn) Upstream() any { func (vc *Conn) NeedHandshake() bool { select { case <-vc.handshake: - return true + return false default: } - return false + return true } func (vc *Conn) IsXTLSVisionEnabled() bool { From 76ccebf0994eaa3fccc072fb8904889cc5b2cf11 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 27 Feb 2023 09:46:16 +0800 Subject: [PATCH 041/149] chore: better REJECT process --- tunnel/tunnel.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 9e9db82b..d80893c9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -406,29 +406,36 @@ func handleTCPConn(connCtx C.ConnContext) { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) defer cancel() - remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) { - remoteConn, err := proxy.DialContext(ctx, dialMetadata) + remoteConn, err := retry(ctx, func(ctx context.Context) (remoteConn C.Conn, err error) { + remoteConn, err = proxy.DialContext(ctx, dialMetadata) if err != nil { - return nil, err - } - for _, chain := range remoteConn.Chains() { - if chain == "REJECT" { - return remoteConn, nil - } + return } + if N.NeedHandshake(remoteConn) { + defer func() { + for _, chain := range remoteConn.Chains() { + if chain == "REJECT" { + err = nil + return + } + } + if err != nil { + remoteConn = nil + } + }() peekMutex.Lock() defer peekMutex.Unlock() peekBytes, _ = conn.Peek(conn.Buffered()) _, err = remoteConn.Write(peekBytes) if err != nil { - return nil, err + return } if peekLen = len(peekBytes); peekLen > 0 { _, _ = conn.Discard(peekLen) } } - return remoteConn, err + return }, func(err error) { if rule == nil { log.Warnln( From ecb2a5f3c6f16b806add47aebbe0072d993e3b53 Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+H1JK@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:02:44 +0800 Subject: [PATCH 042/149] adjust: Simplify VLESS handshake lock --- transport/vless/conn.go | 67 ++++++++++++++++++--------------------- transport/vless/filter.go | 13 ++++---- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 53dc7c85..c8477090 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -33,8 +33,8 @@ type Conn struct { addons *Addons received bool - handshake chan struct{} handshakeMutex sync.Mutex + needHandshake bool err error tlsConn net.Conn @@ -181,19 +181,25 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { } func (vc *Conn) Write(p []byte) (int, error) { - select { - case <-vc.handshake: - default: - if vc.sendRequest(p) { + if vc.needHandshake { + vc.handshakeMutex.Lock() + if vc.needHandshake { + vc.needHandshake = false + if vc.sendRequest(p) { + vc.handshakeMutex.Unlock() + if vc.err != nil { + return 0, vc.err + } + return len(p), vc.err + } if vc.err != nil { + vc.handshakeMutex.Unlock() return 0, vc.err } - return len(p), vc.err - } - if vc.err != nil { - return 0, vc.err } + vc.handshakeMutex.Unlock() } + if vc.writeFilterApplicationData { _buffer := buf.StackNew() defer buf.KeepAlive(_buffer) @@ -210,16 +216,22 @@ func (vc *Conn) Write(p []byte) (int, error) { } func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { - select { - case <-vc.handshake: - default: - if vc.sendRequest(buffer.Bytes()) { - return vc.err - } - if vc.err != nil { - return vc.err + if vc.needHandshake { + vc.handshakeMutex.Lock() + if vc.needHandshake { + vc.needHandshake = false + if vc.sendRequest(buffer.Bytes()) { + vc.handshakeMutex.Unlock() + return vc.err + } + if vc.err != nil { + vc.handshakeMutex.Unlock() + return vc.err + } } + vc.handshakeMutex.Unlock() } + if vc.writeFilterApplicationData { buffer2 := ReshapeBuffer(buffer) defer buffer2.Release() @@ -281,18 +293,6 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { } func (vc *Conn) sendRequest(p []byte) bool { - vc.handshakeMutex.Lock() - defer vc.handshakeMutex.Unlock() - - select { - case <-vc.handshake: - // The handshake has been completed before. - // So return false to remind the caller. - return false - default: - } - defer close(vc.handshake) - var addonsBytes []byte if vc.addons != nil { addonsBytes, vc.err = proto.Marshal(vc.addons) @@ -431,12 +431,7 @@ func (vc *Conn) Upstream() any { } func (vc *Conn) NeedHandshake() bool { - select { - case <-vc.handshake: - return false - default: - } - return true + return vc.needHandshake } func (vc *Conn) IsXTLSVisionEnabled() bool { @@ -451,7 +446,7 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) { Conn: conn, id: client.uuid, dst: dst, - handshake: make(chan struct{}), + needHandshake: true, } if !dst.UDP && client.Addons != nil { diff --git a/transport/vless/filter.go b/transport/vless/filter.go index e16100d5..3ddfb8b9 100644 --- a/transport/vless/filter.go +++ b/transport/vless/filter.go @@ -33,14 +33,13 @@ func (vc *Conn) FilterTLS(buffer []byte) (index int) { } lenP := len(buffer) vc.packetsToFilter-- - if index := bytes.Index(buffer, tlsServerHandshakeStart); index != -1 { + if index = bytes.Index(buffer, tlsServerHandshakeStart); index != -1 { if lenP >= index+5 { if buffer[0] == 22 && buffer[1] == 3 && buffer[2] == 3 { vc.isTLS = true if buffer[5] == tlsHandshakeTypeServerHello { - log.Debugln("isTLS12orAbove") + //log.Debugln("isTLS12orAbove") vc.remainingServerHello = binary.BigEndian.Uint16(buffer[index+3:]) + 5 - vc.isTLS12orAbove = true if lenP-index >= 79 && vc.remainingServerHello >= 79 { sessionIDLen := int(buffer[index+43]) @@ -49,7 +48,7 @@ func (vc *Conn) FilterTLS(buffer []byte) (index int) { } } } - } else if index := bytes.Index(buffer, tlsClientHandshakeStart); index != -1 { + } else if index = bytes.Index(buffer, tlsClientHandshakeStart); index != -1 { if lenP >= index+5 && buffer[index+5] == tlsHandshakeTypeClientHello { vc.isTLS = true } @@ -74,15 +73,15 @@ func (vc *Conn) FilterTLS(buffer []byte) (index int) { if ok && cs != "TLS_AES_128_CCM_8_SHA256" { vc.enableXTLS = true } - log.Debugln("XTLS Vision found TLS 1.3, packetLength= %d CipherSuite= %s", lenP, cs) + log.Debugln("XTLS Vision found TLS 1.3, packetLength=%d, CipherSuite=%s", lenP, cs) vc.packetsToFilter = 0 return } else if vc.remainingServerHello <= 0 { - log.Debugln("XTLS Vision found TLS 1.2, packetLength= %d", lenP) + log.Debugln("XTLS Vision found TLS 1.2, packetLength=%d", lenP) vc.packetsToFilter = 0 return } - log.Debugln("XTLS Vision found inconclusive server hello, packetLength= %d,remainingServerHelloBytes= %d", lenP, vc.remainingServerHello) + log.Debugln("XTLS Vision found inconclusive server hello, packetLength=%d, remainingServerHelloBytes=%d", lenP, vc.remainingServerHello) } if vc.packetsToFilter <= 0 { log.Debugln("XTLS Vision stop filtering") From e3e0e9796b5cca03f97f361f7c17a328586c0573 Mon Sep 17 00:00:00 2001 From: metacubex Date: Mon, 27 Feb 2023 23:02:45 +0800 Subject: [PATCH 043/149] chore: better workflow --- .github/workflows/build.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66b694de..a47ff3fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -221,6 +221,10 @@ jobs: tag: Prerelease-${{ github.ref_name }} deleteOnlyFromDrafts: false + - name: Set Env + run: echo "BUILDTIME=$(date -u)" >> $GITHUB_ENV + shell: bash + - name: Tag Repo uses: richardsimko/update-tag@v1.0.6 with: @@ -237,6 +241,10 @@ jobs: files: bin/* prerelease: true generate_release_notes: true + body: | + Release created at ${{ env.BUILDTIME }} + Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version +
Upload-Release: permissions: write-all From d55025ecaecbfada8feeab0b114db4697e90c1cc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 28 Feb 2023 15:53:23 +0800 Subject: [PATCH 044/149] fix: udp loopback show "The requested address is not valid in its context." --- component/dialer/dialer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 2d3937c8..478e9f19 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -347,7 +347,12 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C } func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, WithOption(d.Opt)) + opt := WithOption(d.Opt) + if rAddrPort.Addr().Unmap().IsLoopback() { + // avoid "The requested address is not valid in its context." + opt = WithInterface("") + } + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, opt) } func NewDialer(options ...Option) Dialer { From 6061f3d4fae1a5d3d7f1ae1a71e6b94fd1093987 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 28 Feb 2023 21:17:52 +0800 Subject: [PATCH 045/149] chore: add more utls fingerprints --- component/tls/utls.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/component/tls/utls.go b/component/tls/utls.go index 4724c9a5..a7189aa8 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -67,7 +67,22 @@ var Fingerprints = map[string]UClientHelloID{ "firefox": {&utls.HelloFirefox_Auto}, "safari": {&utls.HelloSafari_Auto}, "ios": {&utls.HelloIOS_Auto}, - "randomized": {&utls.HelloRandomized}, + "android": {&utls.HelloAndroid_11_OkHttp}, + "edge": {&utls.HelloEdge_Auto}, + "360": {&utls.Hello360_Auto}, + "qq": {&utls.HelloQQ_Auto}, + "random": {nil}, + "randomized": {nil}, +} + +func init() { + weights := utls.DefaultWeights + weights.TLSVersMax_Set_VersionTLS13 = 1 + weights.FirstKeyShare_Set_CurveP256 = 0 + randomized := utls.HelloRandomized + randomized.Seed, _ = utls.NewPRNGSeed() + randomized.Weights = &weights + Fingerprints["randomized"] = UClientHelloID{&randomized} } func copyConfig(c *tls.Config) *utls.Config { From 685fd49dd772bfe02e987418c85cf43e2556f2c6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 1 Mar 2023 13:41:25 +0800 Subject: [PATCH 046/149] chore: better workflow --- .github/workflows/build.yml | 59 ++++++++++++++++++++++--------------- Dockerfile | 4 ++- Makefile | 3 ++ docker/file-name.sh | 17 +++++------ 4 files changed, 49 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a47ff3fa..b40d155a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,35 +37,40 @@ jobs: id: "2", } - { - type: "WithoutCGO", - target: "linux-arm64 linux-mips64 linux-mips64le", - id: "3", - } + type: "WithoutCGO", + target: "linux-arm64 linux-mips64 linux-mips64le", + id: "3", + } - { - type: "WithoutCGO", - target: "linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat", - id: "4", - } + type: "WithoutCGO", + target: "linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat", + id: "4", + } - { - type: "WithoutCGO", - target: "freebsd-386 freebsd-amd64 freebsd-arm64", - id: "5", - } + type: "WithoutCGO", + target: "linux-386 linux-riscv64", + id: "5", + } - { - type: "WithoutCGO", - target: "windows-amd64-compatible windows-amd64 windows-386", - id: "6", - } + type: "WithoutCGO", + target: "freebsd-386 freebsd-amd64 freebsd-arm64", + id: "6", + } - { - type: "WithoutCGO", - target: "windows-arm64 windows-arm32v7", - id: "7", - } + type: "WithoutCGO", + target: "windows-amd64-compatible windows-amd64 windows-386", + id: "7", + } - { - type: "WithoutCGO", - target: "darwin-amd64 darwin-arm64 android-arm64", - id: "8", - } + type: "WithoutCGO", + target: "windows-arm64 windows-arm32v7", + id: "8", + } + - { + type: "WithoutCGO", + target: "darwin-amd64 darwin-arm64 android-arm64", + id: "9", + } - { type: "WithCGO", target: "windows/*", id: "1" } - { type: "WithCGO", target: "linux/386", id: "2" } - { type: "WithCGO", target: "linux/amd64", id: "3" } @@ -193,6 +198,10 @@ jobs: ls -la cd .. + - name: Save version + run: echo ${VERSION} > bin/version.txt + shell: bash + - uses: actions/upload-artifact@v3 if: ${{ success() }} with: @@ -329,5 +338,7 @@ jobs: linux/386 linux/amd64 linux/arm64/v8 + linux/arm/v7 + # linux/riscv64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index 9c2e44c7..f5dcc307 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ FROM alpine:latest as builder +ARG TARGETPLATFORM +RUN echo "I'm building for $TARGETPLATFORM" RUN apk add --no-cache gzip && \ mkdir /clash-config && \ @@ -10,7 +12,7 @@ COPY docker/file-name.sh /clash/file-name.sh WORKDIR /clash COPY bin/ bin/ RUN FILE_NAME=`sh file-name.sh` && echo $FILE_NAME && \ - FILE_NAME=`ls bin/ | egrep "$FILE_NAME.*"|awk NR==1` && \ + FILE_NAME=`ls bin/ | egrep "$FILE_NAME.*"|awk NR==1` && echo $FILE_NAME && \ mv bin/$FILE_NAME clash.gz && gzip -d clash.gz && echo "$FILE_NAME" > /clash-config/test FROM alpine:latest LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/Clash.Meta" diff --git a/Makefile b/Makefile index f7a637a9..ecd1f5e6 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,9 @@ linux-mips64: linux-mips64le: GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ +linux-riscv64: + GOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + android-arm64: GOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ diff --git a/docker/file-name.sh b/docker/file-name.sh index fb87cad0..1ac2cee0 100644 --- a/docker/file-name.sh +++ b/docker/file-name.sh @@ -1,26 +1,25 @@ #!/bin/sh os="clash.meta-linux-" -arch=`uname -m` -case $arch in - "x86_64") +case $TARGETPLATFORM in + "linux/amd64") arch="amd64-compatible" ;; - "x86") - arch="386-cgo" + "linux/386") + arch="386" ;; - "aarch64") + "linux/arm64") arch="arm64" ;; - "armv7l") + "linux/arm/v7") arch="armv7" ;; "riscv64") - arch="riscv64-cgo" + arch="riscv64" ;; *) echo "Unknown architecture" exit 1 ;; esac -file_name="$os$arch" +file_name="$os$arch-$(cat bin/version.txt)" echo $file_name \ No newline at end of file From e7613e4f8b508dc95bbf45a8acbdaf8d8f1f8462 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 1 Mar 2023 14:04:42 +0800 Subject: [PATCH 047/149] fix: loadbalance panic --- adapter/outboundgroup/loadbalance.go | 35 +++++++++++++++------------- adapter/outboundgroup/relay.go | 2 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index b89dba93..7c7edd36 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "sync" "time" "github.com/Dreamacro/clash/adapter/outbound" @@ -20,7 +21,7 @@ import ( "golang.org/x/net/publicsuffix" ) -type strategyFn = func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy +type strategyFn = func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy type LoadBalance struct { *GroupBase @@ -127,21 +128,23 @@ func (lb *LoadBalance) SupportUDP() bool { } func strategyRoundRobin() strategyFn { - flag := true idx := 0 - return func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy { + idxMutex := sync.Mutex{} + return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { + id := idx // value could be wrong due to no lock, but don't care if we don't touch + if touch { + idxMutex.Lock() + defer idxMutex.Unlock() + id = idx // get again by lock's protect, so it must be right + defer func() { + idx = id + }() + } + length := len(proxies) for i := 0; i < length; i++ { - flag = !flag - if flag { - idx = (idx - 1) % length - } else { - idx = (idx + 2) % length - } - if idx < 0 { - idx = idx + length - } - proxy := proxies[idx] + id = (id + 1) % length + proxy := proxies[id] if proxy.Alive() { return proxy } @@ -153,7 +156,7 @@ func strategyRoundRobin() strategyFn { func strategyConsistentHashing() strategyFn { maxRetry := 5 - return func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy { + return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { key := uint64(murmur3.Sum32([]byte(getKey(metadata)))) buckets := int32(len(proxies)) for i := 0; i < maxRetry; i, key = i+1, key+1 { @@ -181,7 +184,7 @@ func strategyStickySessions() strategyFn { lruCache := cache.New[uint64, int]( cache.WithAge[uint64, int](int64(ttl.Seconds())), cache.WithSize[uint64, int](1000)) - return func(proxies []C.Proxy, metadata *C.Metadata) C.Proxy { + return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { key := uint64(murmur3.Sum32([]byte(getKeyWithSrcAndDst(metadata)))) length := len(proxies) idx, has := lruCache.Get(key) @@ -213,7 +216,7 @@ func strategyStickySessions() strategyFn { // Unwrap implements C.ProxyAdapter func (lb *LoadBalance) Unwrap(metadata *C.Metadata, touch bool) C.Proxy { proxies := lb.GetProxies(touch) - return lb.strategyFn(proxies, metadata) + return lb.strategyFn(proxies, metadata, true) } // MarshalJSON implements C.ProxyAdapter diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index b466b1ff..e17ae132 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -176,7 +176,7 @@ func (r *Relay) proxies(metadata *C.Metadata, touch bool) ([]C.Proxy, []C.Proxy) } func (r *Relay) Addr() string { - proxies, _ := r.proxies(nil, true) + proxies, _ := r.proxies(nil, false) return proxies[len(proxies)-1].Addr() } From 4c1682b365ac809c33a91ff54cc7da5d6a5cf909 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:55:26 +0800 Subject: [PATCH 048/149] chore: better release notes --- .github/workflows/build.yml | 84 ++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b40d155a..6a6ca73b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,40 +37,36 @@ jobs: id: "2", } - { - type: "WithoutCGO", - target: "linux-arm64 linux-mips64 linux-mips64le", - id: "3", - } + type: "WithoutCGO", + target: "linux-arm64 linux-mips64 linux-mips64le", + id: "3", + } - { - type: "WithoutCGO", - target: "linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat", - id: "4", - } + type: "WithoutCGO", + target: "linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat", + id: "4", + } + - { type: "WithoutCGO", target: "linux-386 linux-riscv64", id: "5" } - { - type: "WithoutCGO", - target: "linux-386 linux-riscv64", - id: "5", - } + type: "WithoutCGO", + target: "freebsd-386 freebsd-amd64 freebsd-arm64", + id: "6", + } - { - type: "WithoutCGO", - target: "freebsd-386 freebsd-amd64 freebsd-arm64", - id: "6", - } + type: "WithoutCGO", + target: "windows-amd64-compatible windows-amd64 windows-386", + id: "7", + } - { - type: "WithoutCGO", - target: "windows-amd64-compatible windows-amd64 windows-386", - id: "7", - } + type: "WithoutCGO", + target: "windows-arm64 windows-arm32v7", + id: "8", + } - { - type: "WithoutCGO", - target: "windows-arm64 windows-arm32v7", - id: "8", - } - - { - type: "WithoutCGO", - target: "darwin-amd64 darwin-arm64 android-arm64", - id: "9", - } + type: "WithoutCGO", + target: "darwin-amd64 darwin-arm64 android-arm64", + id: "9", + } - { type: "WithCGO", target: "windows/*", id: "1" } - { type: "WithCGO", target: "linux/386", id: "2" } - { type: "WithCGO", target: "linux/amd64", id: "3" } @@ -113,10 +109,11 @@ jobs: - name: Set ENV run: | + sudo timedatectl set-timezone "Asia/Shanghai" echo "NAME=clash.meta" >> $GITHUB_ENV echo "REPO=${{ github.repository }}" >> $GITHUB_ENV echo "ShortSHA=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV - echo "BUILDTIME=$(date -u)" >> $GITHUB_ENV + echo "BUILDTIME=$(date)" >> $GITHUB_ENV echo "BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV shell: bash @@ -211,7 +208,7 @@ jobs: Upload-Prerelease: permissions: write-all if: ${{ github.ref_type=='branch' }} - needs: [ Build ] + needs: [Build] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 @@ -231,7 +228,8 @@ jobs: deleteOnlyFromDrafts: false - name: Set Env - run: echo "BUILDTIME=$(date -u)" >> $GITHUB_ENV + run: | + echo "BUILDTIME=$(date)" >> $GITHUB_ENV shell: bash - name: Tag Repo @@ -241,24 +239,32 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: | + echo 'Release created at ${{ env.BUILDTIME }} + Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version +
+ ### release version + `default(not specified in file name)`: compiled with GOAMD64=v3 + `cgo`: support lwip tun stack, compiled with GOAMD64=v1 + `compatible`: compiled with GOAMD64=v1 + Check details between different architectural levels [here](https://github.com/golang/go/wiki/MinimumRequirements#amd64).' > release.txt + - name: Upload Prerelease uses: softprops/action-gh-release@v1 if: ${{ success() }} with: tag: ${{ github.ref_name }} tag_name: Prerelease-${{ github.ref_name }} - files: bin/* + files: | + bin/* prerelease: true generate_release_notes: true - body: | - Release created at ${{ env.BUILDTIME }} - Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version -
+ body_path: release.txt Upload-Release: permissions: write-all if: ${{ github.ref_type=='tag' }} - needs: [ Build ] + needs: [Build] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 @@ -281,7 +287,7 @@ jobs: Docker: permissions: write-all - needs: [ Build ] + needs: [Build] runs-on: ubuntu-latest steps: - name: Checkout repository From 02395413bacc4630de9757d308b9e07737b0be61 Mon Sep 17 00:00:00 2001 From: kunish Date: Thu, 2 Mar 2023 14:39:57 +0800 Subject: [PATCH 049/149] chore: better release notes --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a6ca73b..ea5c8142 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -240,14 +240,16 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo 'Release created at ${{ env.BUILDTIME }} + cat > release.txt << 'EOF' + Release created at ${{ env.BUILDTIME }} Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version
### release version `default(not specified in file name)`: compiled with GOAMD64=v3 `cgo`: support lwip tun stack, compiled with GOAMD64=v1 `compatible`: compiled with GOAMD64=v1 - Check details between different architectural levels [here](https://github.com/golang/go/wiki/MinimumRequirements#amd64).' > release.txt + Check details between different architectural levels [here](https://github.com/golang/go/wiki/MinimumRequirements#amd64). + EOF - name: Upload Prerelease uses: softprops/action-gh-release@v1 From 838c5c7770a5e5e8b79b41af7868010cf9c99d0d Mon Sep 17 00:00:00 2001 From: kunish Date: Thu, 2 Mar 2023 15:09:40 +0800 Subject: [PATCH 050/149] ci: set prerelease notes timezone of release create time to Asia/Shanghai --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea5c8142..537c7d14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -229,7 +229,7 @@ jobs: - name: Set Env run: | - echo "BUILDTIME=$(date)" >> $GITHUB_ENV + echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV shell: bash - name: Tag Repo From da27be6de5e4780965e807015101c0e483bfbd7d Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 4 Mar 2023 09:44:36 +0800 Subject: [PATCH 051/149] chore: add sni of tuic in demo --- docs/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/config.yaml b/docs/config.yaml index 771532d1..438875d2 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -569,6 +569,7 @@ proxies: # socks5 # fast-open: true # skip-cert-verify: true # max-open-streams: 20 # default 100, too many open streams may hurt performance + # sni: example.com # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss From bccc6aa8d1cb402373ba0b68e665d0f2f8b1894f Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 4 Mar 2023 18:33:05 +0800 Subject: [PATCH 052/149] chore: Better REJECT conn --- adapter/outbound/reject.go | 45 ++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index 43833238..a469ed2a 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -6,6 +6,7 @@ import ( "net" "time" + "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" ) @@ -16,12 +17,12 @@ type Reject struct { // DialContext implements C.ProxyAdapter func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - return NewConn(&nopConn{}, r), nil + return NewConn(nopConn{}, r), nil } // ListenPacketContext implements C.ProxyAdapter func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return newPacketConn(&nopPacketConn{}, r), nil + return newPacketConn(nopPacketConn{}, r), nil } func NewReject() *Reject { @@ -48,27 +49,37 @@ func NewPass() *Reject { type nopConn struct{} -func (rw *nopConn) Read(b []byte) (int, error) { +func (rw nopConn) Read(b []byte) (int, error) { return 0, io.EOF } -func (rw *nopConn) Write(b []byte) (int, error) { +func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { + return io.EOF +} + +func (rw nopConn) Write(b []byte) (int, error) { return 0, io.EOF } -func (rw *nopConn) Close() error { return nil } -func (rw *nopConn) LocalAddr() net.Addr { return nil } -func (rw *nopConn) RemoteAddr() net.Addr { return nil } -func (rw *nopConn) SetDeadline(time.Time) error { return nil } -func (rw *nopConn) SetReadDeadline(time.Time) error { return nil } -func (rw *nopConn) SetWriteDeadline(time.Time) error { return nil } +func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { + return io.EOF +} + +func (rw nopConn) Close() error { return nil } +func (rw nopConn) LocalAddr() net.Addr { return nil } +func (rw nopConn) RemoteAddr() net.Addr { return nil } +func (rw nopConn) SetDeadline(time.Time) error { return nil } +func (rw nopConn) SetReadDeadline(time.Time) error { return nil } +func (rw nopConn) SetWriteDeadline(time.Time) error { return nil } + +var udpAddrIPv4Unspecified = &net.UDPAddr{IP: net.IPv4zero, Port: 0} type nopPacketConn struct{} -func (npc *nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { return len(b), nil } -func (npc *nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, io.EOF } -func (npc *nopPacketConn) Close() error { return nil } -func (npc *nopPacketConn) LocalAddr() net.Addr { return &net.UDPAddr{IP: net.IPv4zero, Port: 0} } -func (npc *nopPacketConn) SetDeadline(time.Time) error { return nil } -func (npc *nopPacketConn) SetReadDeadline(time.Time) error { return nil } -func (npc *nopPacketConn) SetWriteDeadline(time.Time) error { return nil } +func (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { return len(b), nil } +func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, io.EOF } +func (npc nopPacketConn) Close() error { return nil } +func (npc nopPacketConn) LocalAddr() net.Addr { return udpAddrIPv4Unspecified } +func (npc nopPacketConn) SetDeadline(time.Time) error { return nil } +func (npc nopPacketConn) SetReadDeadline(time.Time) error { return nil } +func (npc nopPacketConn) SetWriteDeadline(time.Time) error { return nil } From 8771fa5c17e8eb430739eb125bc283f3dc0b32df Mon Sep 17 00:00:00 2001 From: H1JK <106379370+h1jk@users.noreply.github.com> Date: Sat, 4 Mar 2023 21:47:06 +0800 Subject: [PATCH 053/149] chore: Vision padding upgrade --- transport/vless/conn.go | 28 ++++++++++++++-------------- transport/vless/vision.go | 23 +++++++++++++++++------ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index c8477090..1f7d2cb3 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -241,7 +241,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { command = commandPaddingEnd // disable XTLS - vc.readProcess = false + //vc.readProcess = false vc.writeFilterApplicationData = false vc.packetsToFilter = 0 } else if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) || vc.packetsToFilter <= 0 { @@ -252,7 +252,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { } vc.writeFilterApplicationData = false } - ApplyPadding(buffer, command, nil) + ApplyPadding(buffer, command, nil, vc.isTLS) err := vc.ExtendedWriter.WriteBuffer(buffer) if err != nil { return err @@ -276,7 +276,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error { } vc.writeFilterApplicationData = false } - ApplyPadding(buffer2, command, nil) + ApplyPadding(buffer2, command, nil, vc.isTLS) err = vc.ExtendedWriter.WriteBuffer(buffer2) if vc.writeDirect { vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn) @@ -352,19 +352,19 @@ func (vc *Conn) sendRequest(p []byte) bool { if isVision && !vc.dst.UDP && !vc.dst.Mux { if len(p) == 0 { - WriteWithPadding(buffer, nil, commandPaddingContinue, vc.id) + WriteWithPadding(buffer, nil, commandPaddingContinue, vc.id, vc.isTLS) } else { vc.FilterTLS(p) - if vc.isTLS { - WriteWithPadding(buffer, p, commandPaddingContinue, vc.id) - } else { - buf.Must(buf.Error(buffer.Write(p))) - - // disable XTLS - vc.readProcess = false - vc.writeFilterApplicationData = false - vc.packetsToFilter = 0 - } + //if vc.isTLS { + WriteWithPadding(buffer, p, commandPaddingContinue, vc.id, vc.isTLS) + //} else { + // buf.Must(buf.Error(buffer.Write(p))) + // + // // disable XTLS + // vc.readProcess = false + // vc.writeFilterApplicationData = false + // vc.packetsToFilter = 0 + //} } } else { buf.Must(buf.Error(buffer.Write(p))) diff --git a/transport/vless/vision.go b/transport/vless/vision.go index f87a6870..14d701b4 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -19,16 +19,22 @@ const ( commandPaddingDirect byte = 0x02 ) -func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID) { +func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(len(p)) var paddingLen int32 - if contentLen < 900 { + if contentLen < 900 && paddingTLS { + log.Debugln("long padding") paddingLen = rand.Int31n(500) + 900 - contentLen + } else { + paddingLen = rand.Int31n(256) + } + if paddingLen > buf.BufferSize-21-contentLen { + paddingLen = buf.BufferSize - 21 - contentLen } - if userUUID != nil { // unnecessary, but keep the same with Xray buffer.Write(userUUID.Bytes()) } + buffer.WriteByte(command) binary.BigEndian.PutUint16(buffer.Extend(2), uint16(contentLen)) binary.BigEndian.PutUint16(buffer.Extend(2), uint16(paddingLen)) @@ -37,13 +43,18 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid log.Debugln("XTLS Vision write padding1: command=%v, payloadLen=%v, paddingLen=%v", command, contentLen, paddingLen) } -func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID) { +func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(buffer.Len()) var paddingLen int32 - if contentLen < 900 { + if contentLen < 900 && paddingTLS { + log.Debugln("long padding") paddingLen = rand.Int31n(500) + 900 - contentLen + } else { + paddingLen = rand.Int31n(256) + } + if paddingLen > buf.BufferSize-21-contentLen { + paddingLen = buf.BufferSize - 21 - contentLen } - binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen)) binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen)) buffer.ExtendHeader(1)[0] = command From 3b037acb01eb1e5691c9f4261733a9a4f6189d49 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 4 Mar 2023 23:41:41 +0800 Subject: [PATCH 054/149] chore: Update dependencies --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ listener/sing_tun/server.go | 2 +- listener/sing_tun/server_notwindows.go | 4 ++-- listener/sing_tun/server_windows.go | 4 ++-- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index f9beea8f..0b02e67d 100644 --- a/go.mod +++ b/go.mod @@ -20,15 +20,15 @@ require ( 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.20230226153717-4e80da7e6947 - github.com/metacubex/sing-tun v0.1.1-0.20230222113101-fbfa2dab826d + github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3 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.20230226150041-83d9121b04c6 - github.com/sagernet/sing-shadowtls v0.0.0-20230221130515-dac782ca098e - github.com/sagernet/sing-vmess v0.1.3-0.20230226144228-40c1abdb85be + github.com/sagernet/sing v0.1.8-0.20230303052048-c875a4ffab1a + github.com/sagernet/sing-shadowtls v0.1.0 + github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b 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 @@ -41,7 +41,7 @@ require ( go.uber.org/automaxprocs v1.5.1 golang.org/x/crypto v0.6.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/net v0.6.0 + golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d @@ -63,7 +63,7 @@ require ( github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 // indirect + github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 1875b574..d2aae570 100644 --- a/go.sum +++ b/go.sum @@ -87,14 +87,14 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65 h1:WUINdCB/UvSX9I+wN+y5xVEisPrXA73rxkoHK5DMyZs= -github.com/metacubex/gvisor v0.0.0-20230222112937-bdbcd206ec65/go.mod h1:e3lCxh3TozKMWAsYTC7nBVnepAxPL/sNyScUFvmEoec= +github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005 h1:0TEvReK/D6YLszjGj/bdx4d7amQSjQ2X/98r4ZiUbxU= +github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= 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.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-tun v0.1.1-0.20230304153753-5058534177f3 h1:oQLThm1a8E7hHmoM9XF2cO0FZPsHVynC4YXW4b3liUI= +github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3/go.mod h1:b/19bRRhwampNPV+1gVDyDsQHmuGDaplxPQkwJh1kj4= github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 h1:d96mCF/LYyC9kULd2xwcXfP0Jd8klrOngmRxuUIZg/8= github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4/go.mod h1:p2VpJuxRefgVMxc8cmatMGSFNvYbjMYMsXJOe7qFstw= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= @@ -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.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.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/sing v0.1.8-0.20230303052048-c875a4ffab1a h1:NvhI/8DMFt2yV3eoYhw6P/XyWzzIKkMiGvFglJbWHWg= +github.com/sagernet/sing v0.1.8-0.20230303052048-c875a4ffab1a/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= +github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= +github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b h1:NZeF0ATeJwe4W3gTJNeIfTB6yBxai665q1HvDOaWmmU= +github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b/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= @@ -190,8 +190,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 3abef12d..06215c73 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -221,7 +221,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte err = E.Cause(err, "build android rules") return } - tunIf, err := tunOpen(tunOptions) + tunIf, err := tunNew(tunOptions) if err != nil { err = E.Cause(err, "configure tun interface") return diff --git a/listener/sing_tun/server_notwindows.go b/listener/sing_tun/server_notwindows.go index d3280c5c..eda79dc0 100644 --- a/listener/sing_tun/server_notwindows.go +++ b/listener/sing_tun/server_notwindows.go @@ -6,6 +6,6 @@ import ( tun "github.com/metacubex/sing-tun" ) -func tunOpen(options tun.Options) (tun.Tun, error) { - return tun.Open(options) +func tunNew(options tun.Options) (tun.Tun, error) { + return tun.New(options) } diff --git a/listener/sing_tun/server_windows.go b/listener/sing_tun/server_windows.go index 7b745cac..9584f32f 100644 --- a/listener/sing_tun/server_windows.go +++ b/listener/sing_tun/server_windows.go @@ -8,11 +8,11 @@ import ( tun "github.com/metacubex/sing-tun" ) -func tunOpen(options tun.Options) (tunIf tun.Tun, err error) { +func tunNew(options tun.Options) (tunIf tun.Tun, err error) { maxRetry := 3 for i := 0; i < maxRetry; i++ { timeBegin := time.Now() - tunIf, err = tun.Open(options) + tunIf, err = tun.New(options) if err == nil { return } From ae966833a4066c10df803d1ff157fd0782407596 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sun, 5 Mar 2023 11:00:14 +0800 Subject: [PATCH 055/149] chore: Generate UUID from fastrand --- adapter/outbound/base.go | 5 ++--- adapter/provider/healthcheck.go | 4 ++-- common/convert/util.go | 7 ++++--- common/utils/uuid.go | 11 +++++++++-- context/conn.go | 3 ++- context/dns.go | 3 ++- context/packetconn.go | 3 ++- go.mod | 1 + go.sum | 2 ++ transport/tuic/server.go | 9 +++++---- tunnel/statistic/tracker.go | 5 +++-- 11 files changed, 34 insertions(+), 19 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index d51655d8..b69311f6 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -8,10 +8,9 @@ import ( "strings" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" - - "github.com/gofrs/uuid" ) type Base struct { @@ -35,7 +34,7 @@ func (b *Base) Name() string { // Id implements C.ProxyAdapter func (b *Base) Id() string { if b.id == "" { - id, err := uuid.NewV6() + id, err := utils.UnsafeUUIDGenerator.NewV6() if err != nil { b.id = b.name } else { diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index 16b9ad61..6f79cd05 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -6,10 +6,10 @@ import ( "github.com/Dreamacro/clash/common/batch" "github.com/Dreamacro/clash/common/singledo" + "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "github.com/gofrs/uuid" "go.uber.org/atomic" ) @@ -77,7 +77,7 @@ func (hc *HealthCheck) touch() { func (hc *HealthCheck) check() { _, _, _ = hc.singleDo.Do(func() (struct{}, error) { id := "" - if uid, err := uuid.NewV4(); err == nil { + if uid, err := utils.UnsafeUUIDGenerator.NewV4(); err == nil { id = uid.String() } log.Debugln("Start New Health Checking {%s}", id) diff --git a/common/convert/util.go b/common/convert/util.go index 282d52d5..47e8d992 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -2,13 +2,14 @@ package convert import ( "encoding/base64" - "github.com/metacubex/sing-shadowsocks/shadowimpl" "math/rand" "net/http" "strings" "time" - "github.com/gofrs/uuid" + "github.com/Dreamacro/clash/common/utils" + + "github.com/metacubex/sing-shadowsocks/shadowimpl" ) var hostsSuffix = []string{ @@ -293,7 +294,7 @@ var ( ) func RandHost() string { - id, _ := uuid.NewV4() + id, _ := utils.UnsafeUUIDGenerator.NewV4() base := strings.ToLower(base64.RawURLEncoding.EncodeToString(id.Bytes())) base = strings.ReplaceAll(base, "-", "") base = strings.ReplaceAll(base, "_", "") diff --git a/common/utils/uuid.go b/common/utils/uuid.go index 66e176ed..d043ae37 100644 --- a/common/utils/uuid.go +++ b/common/utils/uuid.go @@ -2,15 +2,22 @@ package utils import ( "github.com/gofrs/uuid" + "github.com/zhangyunhao116/fastrand" ) -var uuidNamespace, _ = uuid.FromString("00000000-0000-0000-0000-000000000000") +type fastRandReader struct{} + +func (r fastRandReader) Read(p []byte) (int, error) { + return fastrand.Read(p) +} + +var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{})) // UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090 func UUIDMap(str string) (uuid.UUID, error) { u, err := uuid.FromString(str) if err != nil { - return uuid.NewV5(uuidNamespace, str), nil + return UnsafeUUIDGenerator.NewV5(uuid.Nil, str), nil } return u, nil } diff --git a/context/conn.go b/context/conn.go index b695ac4d..35ea788d 100644 --- a/context/conn.go +++ b/context/conn.go @@ -1,6 +1,7 @@ package context import ( + "github.com/Dreamacro/clash/common/utils" "net" N "github.com/Dreamacro/clash/common/net" @@ -16,7 +17,7 @@ type ConnContext struct { } func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext { - id, _ := uuid.NewV4() + id, _ := utils.UnsafeUUIDGenerator.NewV4() return &ConnContext{ id: id, diff --git a/context/dns.go b/context/dns.go index 59130961..80a4c988 100644 --- a/context/dns.go +++ b/context/dns.go @@ -2,6 +2,7 @@ package context import ( "context" + "github.com/Dreamacro/clash/common/utils" "github.com/gofrs/uuid" "github.com/miekg/dns" @@ -22,7 +23,7 @@ type DNSContext struct { } func NewDNSContext(ctx context.Context, msg *dns.Msg) *DNSContext { - id, _ := uuid.NewV4() + id, _ := utils.UnsafeUUIDGenerator.NewV4() return &DNSContext{ Context: ctx, diff --git a/context/packetconn.go b/context/packetconn.go index 3b005141..87a9290a 100644 --- a/context/packetconn.go +++ b/context/packetconn.go @@ -3,6 +3,7 @@ package context import ( "net" + "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/gofrs/uuid" @@ -15,7 +16,7 @@ type PacketConnContext struct { } func NewPacketConnContext(metadata *C.Metadata) *PacketConnContext { - id, _ := uuid.NewV4() + id, _ := utils.UnsafeUUIDGenerator.NewV4() return &PacketConnContext{ id: id, metadata: metadata, diff --git a/go.mod b/go.mod index 0b02e67d..1444da40 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 + github.com/zhangyunhao116/fastrand v0.3.0 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 diff --git a/go.sum b/go.sum index d2aae570..7774aa64 100644 --- a/go.sum +++ b/go.sum @@ -162,6 +162,8 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= +github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 2830b324..e8dee8d6 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -11,13 +11,14 @@ import ( "sync/atomic" "time" - "github.com/gofrs/uuid" - "github.com/metacubex/quic-go" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" + + "github.com/gofrs/uuid" + "github.com/metacubex/quic-go" ) type ServerOption struct { @@ -55,7 +56,7 @@ func (s *Server) Serve() error { return err } SetCongestionController(conn, s.CongestionController) - uuid, err := uuid.NewV4() + uuid, err := utils.UnsafeUUIDGenerator.NewV4() if err != nil { return err } diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index e21868c5..bb5678b8 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -6,6 +6,7 @@ import ( "github.com/Dreamacro/clash/common/buf" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/gofrs/uuid" @@ -82,7 +83,7 @@ func (tt *tcpTracker) Upstream() any { } func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *tcpTracker { - uuid, _ := uuid.NewV4() + uuid, _ := utils.UnsafeUUIDGenerator.NewV4() if conn != nil { if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { metadata.RemoteDst = tcpAddr.IP.String() @@ -148,7 +149,7 @@ func (ut *udpTracker) Close() error { } func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *udpTracker { - uuid, _ := uuid.NewV4() + uuid, _ := utils.UnsafeUUIDGenerator.NewV4() metadata.RemoteDst = conn.RemoteDestination() ut := &udpTracker{ From 2f36c9d66dd2ddd458e99fcb2953b9c0da02078b Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 6 Mar 2023 00:49:34 +0800 Subject: [PATCH 056/149] chore: better workflow --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 537c7d14..dc43a10e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,11 @@ on: - Alpha - Beta - Meta + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + env: REGISTRY: docker.io jobs: From ad6336231ccc98cf0fdbcaedf88d5585439054a4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 6 Mar 2023 12:59:53 +0800 Subject: [PATCH 057/149] doc: update config.yaml --- docs/config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/config.yaml b/docs/config.yaml index 438875d2..13c5a83c 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -550,6 +550,8 @@ proxies: # socks5 public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= udp: true reserved: "U4An" + # 数组格式也是合法的 + # reserved: [209,98,59] # tuic - name: tuic From 6a97ab9ecb83cfb84e35167cd92b90a5c63c54c2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 6 Mar 2023 18:10:14 +0800 Subject: [PATCH 058/149] chore: use fastrand to replace math/rand --- common/convert/util.go | 6 +++--- common/pool/alloc_test.go | 4 ++-- component/resolver/resolver.go | 8 ++++---- dns/client.go | 4 ++-- dns/resolver.go | 8 ++++---- transport/hysteria/conns/udp/hop.go | 7 ++++--- transport/hysteria/conns/wechat/obfs.go | 10 ++++++---- transport/hysteria/core/client.go | 12 ++++++------ transport/hysteria/obfs/xplus.go | 17 +++++------------ transport/simple-obfs/http.go | 7 ++++--- transport/simple-obfs/tls.go | 11 ++++------- transport/ssr/obfs/http_simple.go | 11 ++++++----- transport/ssr/obfs/random_head.go | 7 ++++--- transport/ssr/obfs/tls1.2_ticket_auth.go | 11 ++++++----- transport/ssr/protocol/auth_aes128_sha1.go | 17 +++++++++-------- transport/ssr/protocol/auth_sha1_v4.go | 7 ++++--- transport/ssr/protocol/base.go | 7 ++++--- transport/ssr/protocol/protocol.go | 5 +++-- transport/tuic/client.go | 8 ++++---- transport/tuic/congestion/bbr_sender.go | 4 ++-- transport/vless/vision.go | 11 +++++------ transport/vmess/conn.go | 12 ++++-------- transport/vmess/h2.go | 4 ++-- transport/vmess/http.go | 9 +++++---- transport/vmess/vmess.go | 7 ++++--- transport/vmess/websocket.go | 6 +++--- 26 files changed, 109 insertions(+), 111 deletions(-) diff --git a/common/convert/util.go b/common/convert/util.go index 47e8d992..4f86e616 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -2,7 +2,6 @@ package convert import ( "encoding/base64" - "math/rand" "net/http" "strings" "time" @@ -10,6 +9,7 @@ import ( "github.com/Dreamacro/clash/common/utils" "github.com/metacubex/sing-shadowsocks/shadowimpl" + "github.com/zhangyunhao116/fastrand" ) var hostsSuffix = []string{ @@ -303,11 +303,11 @@ func RandHost() string { prefix += string(buf[6:8]) + "-" prefix += string(buf[len(buf)-8:]) - return prefix + hostsSuffix[rand.Intn(hostsLen)] + return prefix + hostsSuffix[fastrand.Intn(hostsLen)] } func RandUserAgent() string { - return userAgents[rand.Intn(uaLen)] + return userAgents[fastrand.Intn(uaLen)] } func SetUserAgent(header http.Header) { diff --git a/common/pool/alloc_test.go b/common/pool/alloc_test.go index 3d063315..30aa5c53 100644 --- a/common/pool/alloc_test.go +++ b/common/pool/alloc_test.go @@ -1,10 +1,10 @@ package pool import ( - "math/rand" "testing" "github.com/stretchr/testify/assert" + "github.com/zhangyunhao116/fastrand" ) func TestAllocGet(t *testing.T) { @@ -43,6 +43,6 @@ func TestAllocPutThenGet(t *testing.T) { func BenchmarkMSB(b *testing.B) { for i := 0; i < b.N; i++ { - msb(rand.Int()) + msb(fastrand.Int()) } } diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index fa1e7c02..3fd53527 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math/rand" "net" "net/netip" "strings" @@ -13,6 +12,7 @@ import ( "github.com/Dreamacro/clash/component/trie" "github.com/miekg/dns" + "github.com/zhangyunhao116/fastrand" ) var ( @@ -96,7 +96,7 @@ func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } // ResolveIPv4 with a host, return ipv4 @@ -153,7 +153,7 @@ func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { @@ -202,7 +202,7 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip. } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } // ResolveIP with a host, return ip diff --git a/dns/client.go b/dns/client.go index c5a52281..936a5882 100644 --- a/dns/client.go +++ b/dns/client.go @@ -4,7 +4,6 @@ import ( "context" "crypto/tls" "fmt" - "math/rand" "net" "net/netip" "strings" @@ -16,6 +15,7 @@ import ( "github.com/Dreamacro/clash/component/resolver" D "github.com/miekg/dns" + "github.com/zhangyunhao116/fastrand" ) type client struct { @@ -68,7 +68,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) } else if len(ips) == 0 { return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) } - ip = ips[rand.Intn(len(ips))] + ip = ips[fastrand.Intn(len(ips))] } network := "udp" diff --git a/dns/resolver.go b/dns/resolver.go index ac8917ca..59b1ee06 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math/rand" "net/netip" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" + "github.com/zhangyunhao116/fastrand" "golang.org/x/sync/singleflight" ) @@ -113,7 +113,7 @@ func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, e } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } // LookupIPv4 request with TypeA @@ -129,7 +129,7 @@ func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } // LookupIPv6 request with TypeAAAA @@ -145,7 +145,7 @@ func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } - return ips[rand.Intn(len(ips))], nil + return ips[fastrand.Intn(len(ips))], nil } func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index 53830ae4..447a7592 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -2,7 +2,6 @@ package udp import ( "errors" - "math/rand" "net" "strconv" "strings" @@ -12,6 +11,8 @@ import ( "github.com/Dreamacro/clash/transport/hysteria/obfs" "github.com/Dreamacro/clash/transport/hysteria/utils" + + "github.com/zhangyunhao116/fastrand" ) const ( @@ -85,7 +86,7 @@ func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterva serverAddrs: serverAddrs, hopInterval: hopInterval, obfs: obfs, - addrIndex: rand.Intn(len(serverAddrs)), + addrIndex: fastrand.Intn(len(serverAddrs)), recvQueue: make(chan *udpPacket, packetQueueSize), closeChan: make(chan struct{}), bufPool: sync.Pool{ @@ -176,7 +177,7 @@ func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Ad _ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize) } go c.recvRoutine(c.currentConn) - c.addrIndex = rand.Intn(len(c.serverAddrs)) + c.addrIndex = fastrand.Intn(len(c.serverAddrs)) } func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { diff --git a/transport/hysteria/conns/wechat/obfs.go b/transport/hysteria/conns/wechat/obfs.go index 815aa52f..d13cca55 100644 --- a/transport/hysteria/conns/wechat/obfs.go +++ b/transport/hysteria/conns/wechat/obfs.go @@ -2,12 +2,14 @@ package wechat import ( "encoding/binary" - "github.com/Dreamacro/clash/log" - "github.com/Dreamacro/clash/transport/hysteria/obfs" - "math/rand" "net" "sync" "time" + + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + + "github.com/zhangyunhao116/fastrand" ) const udpBufferSize = 65535 @@ -29,7 +31,7 @@ func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChat obfs: obfs, readBuf: make([]byte, udpBufferSize), writeBuf: make([]byte, udpBufferSize), - sn: rand.Uint32() & 0xFFFF, + sn: fastrand.Uint32() & 0xFFFF, } } diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 1df14242..e0f3a0dd 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -6,20 +6,20 @@ import ( "crypto/tls" "errors" "fmt" - "math/rand" "net" "strconv" "sync" "time" - "github.com/lunixbochs/struc" - "github.com/metacubex/quic-go" - "github.com/metacubex/quic-go/congestion" - "github.com/Dreamacro/clash/transport/hysteria/obfs" "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" "github.com/Dreamacro/clash/transport/hysteria/transport" "github.com/Dreamacro/clash/transport/hysteria/utils" + + "github.com/lunixbochs/struc" + "github.com/metacubex/quic-go" + "github.com/metacubex/quic-go/congestion" + "github.com/zhangyunhao116/fastrand" ) var ( @@ -408,7 +408,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { if err != nil { if errSize, ok := err.(quic.ErrMessageTooLarge); ok { // need to frag - msg.MsgID = uint16(rand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 + msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 fragMsgs := fragUDPMessage(msg, int(errSize)) for _, fragMsg := range fragMsgs { msgBuf.Reset() diff --git a/transport/hysteria/obfs/xplus.go b/transport/hysteria/obfs/xplus.go index dd636452..171bf281 100644 --- a/transport/hysteria/obfs/xplus.go +++ b/transport/hysteria/obfs/xplus.go @@ -2,9 +2,8 @@ package obfs import ( "crypto/sha256" - "math/rand" - "sync" - "time" + + "github.com/zhangyunhao116/fastrand" ) // [salt][obfuscated payload] @@ -12,16 +11,12 @@ import ( const saltLen = 16 type XPlusObfuscator struct { - Key []byte - RandSrc *rand.Rand - - lk sync.Mutex + Key []byte } func NewXPlusObfuscator(key []byte) *XPlusObfuscator { return &XPlusObfuscator{ - Key: key, - RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())), + Key: key, } } @@ -40,9 +35,7 @@ func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { } func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { - x.lk.Lock() - _, _ = x.RandSrc.Read(out[:saltLen]) // salt - x.lk.Unlock() + _, _ = fastrand.Read(out[:saltLen]) // salt // Obfuscate the payload key := sha256.Sum256(append(x.Key, out[:saltLen]...)) for i, c := range in { diff --git a/transport/simple-obfs/http.go b/transport/simple-obfs/http.go index a06bad23..80db34ba 100644 --- a/transport/simple-obfs/http.go +++ b/transport/simple-obfs/http.go @@ -5,11 +5,12 @@ import ( "encoding/base64" "fmt" "io" - "math/rand" "net" "net/http" "github.com/Dreamacro/clash/common/pool" + + "github.com/zhangyunhao116/fastrand" ) // HTTPObfs is shadowsocks http simple-obfs implementation @@ -63,9 +64,9 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) { func (ho *HTTPObfs) Write(b []byte) (int, error) { if ho.firstRequest { randBytes := make([]byte, 16) - rand.Read(randBytes) + fastrand.Read(randBytes) req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) - req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2)) + req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2)) req.Header.Set("Upgrade", "websocket") req.Header.Set("Connection", "Upgrade") req.Host = ho.host diff --git a/transport/simple-obfs/tls.go b/transport/simple-obfs/tls.go index fed8a483..f41e3263 100644 --- a/transport/simple-obfs/tls.go +++ b/transport/simple-obfs/tls.go @@ -4,16 +4,13 @@ import ( "bytes" "encoding/binary" "io" - "math/rand" "net" "time" "github.com/Dreamacro/clash/common/pool" -) -func init() { - rand.Seed(time.Now().Unix()) -} + "github.com/zhangyunhao116/fastrand" +) const ( chunkSize = 1 << 14 // 2 ** 14 == 16 * 1024 @@ -130,8 +127,8 @@ func NewTLSObfs(conn net.Conn, server string) net.Conn { func makeClientHelloMsg(data []byte, server string) []byte { random := make([]byte, 28) sessionID := make([]byte, 32) - rand.Read(random) - rand.Read(sessionID) + fastrand.Read(random) + fastrand.Read(sessionID) buf := &bytes.Buffer{} diff --git a/transport/ssr/obfs/http_simple.go b/transport/ssr/obfs/http_simple.go index c1ea7673..c91cca49 100644 --- a/transport/ssr/obfs/http_simple.go +++ b/transport/ssr/obfs/http_simple.go @@ -4,12 +4,13 @@ import ( "bytes" "encoding/hex" "io" - "math/rand" "net" "strconv" "strings" "github.com/Dreamacro/clash/common/pool" + + "github.com/zhangyunhao116/fastrand" ) func init() { @@ -81,7 +82,7 @@ func (c *httpConn) Write(b []byte) (int, error) { bLength := len(b) headDataLength := bLength if bLength-headLength > 64 { - headDataLength = headLength + rand.Intn(65) + headDataLength = headLength + fastrand.Intn(65) } headData := b[:headDataLength] b = b[headDataLength:] @@ -99,7 +100,7 @@ func (c *httpConn) Write(b []byte) (int, error) { } } hosts := strings.Split(host, ",") - host = hosts[rand.Intn(len(hosts))] + host = hosts[fastrand.Intn(len(hosts))] buf := pool.GetBuffer() defer pool.PutBuffer(buf) @@ -118,7 +119,7 @@ func (c *httpConn) Write(b []byte) (int, error) { buf.WriteString(body + "\r\n\r\n") } else { buf.WriteString("User-Agent: ") - buf.WriteString(userAgent[rand.Intn(len(userAgent))]) + buf.WriteString(userAgent[fastrand.Intn(len(userAgent))]) buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n") if c.post { packBoundary(buf) @@ -146,7 +147,7 @@ func packBoundary(buf *bytes.Buffer) { buf.WriteString("Content-Type: multipart/form-data; boundary=") set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for i := 0; i < 32; i++ { - buf.WriteByte(set[rand.Intn(62)]) + buf.WriteByte(set[fastrand.Intn(62)]) } buf.WriteString("\r\n") } diff --git a/transport/ssr/obfs/random_head.go b/transport/ssr/obfs/random_head.go index b10b01c5..4c55d951 100644 --- a/transport/ssr/obfs/random_head.go +++ b/transport/ssr/obfs/random_head.go @@ -3,10 +3,11 @@ package obfs import ( "encoding/binary" "hash/crc32" - "math/rand" "net" "github.com/Dreamacro/clash/common/pool" + + "github.com/zhangyunhao116/fastrand" ) func init() { @@ -53,10 +54,10 @@ func (c *randomHeadConn) Write(b []byte) (int, error) { c.buf = append(c.buf, b...) if !c.hasSentHeader { c.hasSentHeader = true - dataLength := rand.Intn(96) + 4 + dataLength := fastrand.Intn(96) + 4 buf := pool.Get(dataLength + 4) defer pool.Put(buf) - rand.Read(buf[:dataLength]) + fastrand.Read(buf[:dataLength]) binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength])) _, err := c.Conn.Write(buf) return len(b), err diff --git a/transport/ssr/obfs/tls1.2_ticket_auth.go b/transport/ssr/obfs/tls1.2_ticket_auth.go index 10f2786a..af945133 100644 --- a/transport/ssr/obfs/tls1.2_ticket_auth.go +++ b/transport/ssr/obfs/tls1.2_ticket_auth.go @@ -4,13 +4,14 @@ import ( "bytes" "crypto/hmac" "encoding/binary" - "math/rand" "net" "strings" "time" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/transport/ssr/tools" + + "github.com/zhangyunhao116/fastrand" ) func init() { @@ -25,7 +26,7 @@ type tls12Ticket struct { func newTLS12Ticket(b *Base) Obfs { r := &tls12Ticket{Base: b, authData: &authData{}} - rand.Read(r.clientID[:]) + fastrand.Read(r.clientID[:]) return r } @@ -90,7 +91,7 @@ func (c *tls12TicketConn) Write(b []byte) (int, error) { buf := pool.GetBuffer() defer pool.PutBuffer(buf) for len(b) > 2048 { - size := rand.Intn(4096) + 100 + size := fastrand.Intn(4096) + 100 if len(b) < size { size = len(b) } @@ -196,7 +197,7 @@ func packSNIData(buf *bytes.Buffer, u string) { } func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) { - length := 16 * (rand.Intn(17) + 8) + length := 16 * (fastrand.Intn(17) + 8) buf.Write([]byte{0, 0x23}) binary.Write(buf, binary.BigEndian, uint16(length)) tools.AppendRandBytes(buf, length) @@ -221,6 +222,6 @@ func (t *tls12Ticket) getHost() string { host = "" } hosts := strings.Split(host, ",") - host = hosts[rand.Intn(len(hosts))] + host = hosts[fastrand.Intn(len(hosts))] return host } diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go index 7b4da962..4de48151 100644 --- a/transport/ssr/protocol/auth_aes128_sha1.go +++ b/transport/ssr/protocol/auth_aes128_sha1.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/binary" "math" - "math/rand" "net" "strconv" "strings" @@ -12,6 +11,8 @@ import ( "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/ssr/tools" + + "github.com/zhangyunhao116/fastrand" ) type ( @@ -64,7 +65,7 @@ func (a *authAES128) initUserData() { } if len(a.userKey) == 0 { a.userKey = a.Key - rand.Read(a.userID[:]) + fastrand.Read(a.userID[:]) } } @@ -198,7 +199,7 @@ func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength } func trapezoidRandom(max int, d float64) int { - base := rand.Float64() + base := fastrand.Float64() if d-0 > 1e-6 { a := 1 - d base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d) @@ -219,10 +220,10 @@ func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int if revLength > -1460 { return trapezoidRandom(revLength+1460, -0.3) } - return rand.Intn(32) + return fastrand.Intn(32) } if dataLength > 900 { - return rand.Intn(revLength) + return fastrand.Intn(revLength) } return trapezoidRandom(revLength, -0.3) } @@ -247,7 +248,7 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { copy(macKey, a.iv) copy(macKey[len(a.iv):], a.Key) - poolBuf.WriteByte(byte(rand.Intn(256))) + poolBuf.WriteByte(byte(fastrand.Intn(256))) poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6]) poolBuf.Write(a.userID[:]) err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt) @@ -263,9 +264,9 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { func (a *authAES128) getRandDataLengthForPackAuthData(size int) int { if size > 400 { - return rand.Intn(512) + return fastrand.Intn(512) } - return rand.Intn(1024) + return fastrand.Intn(1024) } func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) { diff --git a/transport/ssr/protocol/auth_sha1_v4.go b/transport/ssr/protocol/auth_sha1_v4.go index 30392c9e..9e814ac2 100644 --- a/transport/ssr/protocol/auth_sha1_v4.go +++ b/transport/ssr/protocol/auth_sha1_v4.go @@ -5,11 +5,12 @@ import ( "encoding/binary" "hash/adler32" "hash/crc32" - "math/rand" "net" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/transport/ssr/tools" + + "github.com/zhangyunhao116/fastrand" ) func init() { @@ -176,7 +177,7 @@ func (a *authSHA1V4) getRandDataLength(size int) int { return 0 } if size > 400 { - return rand.Intn(256) + return fastrand.Intn(256) } - return rand.Intn(512) + return fastrand.Intn(512) } diff --git a/transport/ssr/protocol/base.go b/transport/ssr/protocol/base.go index 4bf799b3..a826bec8 100644 --- a/transport/ssr/protocol/base.go +++ b/transport/ssr/protocol/base.go @@ -6,13 +6,14 @@ import ( "crypto/cipher" "encoding/base64" "encoding/binary" - "math/rand" "sync" "time" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/shadowsocks/core" + + "github.com/zhangyunhao116/fastrand" ) type Base struct { @@ -37,8 +38,8 @@ func (a *authData) next() *authData { a.mutex.Lock() defer a.mutex.Unlock() if a.connectionID > 0xff000000 || a.connectionID == 0 { - rand.Read(a.clientID[:]) - a.connectionID = rand.Uint32() & 0xffffff + fastrand.Read(a.clientID[:]) + a.connectionID = fastrand.Uint32() & 0xffffff } a.connectionID++ copy(r.clientID[:], a.clientID[:]) diff --git a/transport/ssr/protocol/protocol.go b/transport/ssr/protocol/protocol.go index 41bd984c..5b86ecb9 100644 --- a/transport/ssr/protocol/protocol.go +++ b/transport/ssr/protocol/protocol.go @@ -4,8 +4,9 @@ import ( "bytes" "errors" "fmt" - "math/rand" "net" + + "github.com/zhangyunhao116/fastrand" ) var ( @@ -68,7 +69,7 @@ func getHeadSize(b []byte, defaultValue int) int { func getDataLength(b []byte) int { bLength := len(b) - dataLength := getHeadSize(b, 30) + rand.Intn(32) + dataLength := getHeadSize(b, 30) + fastrand.Intn(32) if bLength < dataLength { return bLength } diff --git a/transport/tuic/client.go b/transport/tuic/client.go index d3f511df..4932dc9b 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -6,19 +6,19 @@ import ( "context" "crypto/tls" "errors" - "math/rand" "net" "runtime" "sync" "sync/atomic" "time" - "github.com/metacubex/quic-go" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + + "github.com/metacubex/quic-go" + "github.com/zhangyunhao116/fastrand" ) var ( @@ -352,7 +352,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met pipe1, pipe2 := net.Pipe() var connId uint32 for { - connId = rand.Uint32() + connId = fastrand.Uint32() _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) if !loaded { break diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index 5adbd0b7..d848a9a8 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -5,11 +5,11 @@ package congestion import ( "fmt" "math" - "math/rand" "net" "time" "github.com/metacubex/quic-go/congestion" + "github.com/zhangyunhao116/fastrand" ) const ( @@ -780,7 +780,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is // excluded because in that case increased gain and decreased gain would not // follow each other. - b.cycleCurrentOffset = rand.Int() % (GainCycleLength - 1) + b.cycleCurrentOffset = fastrand.Int() % (GainCycleLength - 1) if b.cycleCurrentOffset >= 1 { b.cycleCurrentOffset += 1 } diff --git a/transport/vless/vision.go b/transport/vless/vision.go index 14d701b4..d817c912 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -3,12 +3,11 @@ package vless import ( "bytes" "encoding/binary" - "math/rand" - "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/log" "github.com/gofrs/uuid" + "github.com/zhangyunhao116/fastrand" ) const ( @@ -24,9 +23,9 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid var paddingLen int32 if contentLen < 900 && paddingTLS { log.Debugln("long padding") - paddingLen = rand.Int31n(500) + 900 - contentLen + paddingLen = fastrand.Int31n(500) + 900 - contentLen } else { - paddingLen = rand.Int31n(256) + paddingLen = fastrand.Int31n(256) } if paddingLen > buf.BufferSize-21-contentLen { paddingLen = buf.BufferSize - 21 - contentLen @@ -48,9 +47,9 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding var paddingLen int32 if contentLen < 900 && paddingTLS { log.Debugln("long padding") - paddingLen = rand.Int31n(500) + 900 - contentLen + paddingLen = fastrand.Int31n(500) + 900 - contentLen } else { - paddingLen = rand.Int31n(256) + paddingLen = fastrand.Int31n(256) } if paddingLen > buf.BufferSize-21-contentLen { paddingLen = buf.BufferSize - 21 - contentLen diff --git a/transport/vmess/conn.go b/transport/vmess/conn.go index cc3155ee..292137ab 100644 --- a/transport/vmess/conn.go +++ b/transport/vmess/conn.go @@ -11,17 +11,13 @@ import ( "errors" "hash/fnv" "io" - "math/rand" "net" "time" + "github.com/zhangyunhao116/fastrand" "golang.org/x/crypto/chacha20poly1305" ) -func init() { - rand.Seed(time.Now().UnixNano()) -} - // Conn wrapper a net.Conn with vmess protocol type Conn struct { net.Conn @@ -76,7 +72,7 @@ func (vc *Conn) sendRequest() error { buf.WriteByte(vc.respV) buf.WriteByte(OptionChunkStream) - p := rand.Intn(16) + p := fastrand.Intn(16) // P Sec Reserve Cmd buf.WriteByte(byte(p<<4) | byte(vc.security)) buf.WriteByte(0) @@ -94,7 +90,7 @@ func (vc *Conn) sendRequest() error { // padding if p > 0 { padding := make([]byte, p) - rand.Read(padding) + fastrand.Read(padding) buf.Write(padding) } @@ -200,7 +196,7 @@ func hashTimestamp(t time.Time) []byte { // newConn return a Conn instance func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) { randBytes := make([]byte, 33) - rand.Read(randBytes) + fastrand.Read(randBytes) reqBodyIV := make([]byte, 16) reqBodyKey := make([]byte, 16) copy(reqBodyIV[:], randBytes[:16]) diff --git a/transport/vmess/h2.go b/transport/vmess/h2.go index d4e81607..6901f61e 100644 --- a/transport/vmess/h2.go +++ b/transport/vmess/h2.go @@ -2,11 +2,11 @@ package vmess import ( "io" - "math/rand" "net" "net/http" "net/url" + "github.com/zhangyunhao116/fastrand" "golang.org/x/net/http2" ) @@ -26,7 +26,7 @@ type H2Config struct { func (hc *h2Conn) establishConn() error { preader, pwriter := io.Pipe() - host := hc.cfg.Hosts[rand.Intn(len(hc.cfg.Hosts))] + host := hc.cfg.Hosts[fastrand.Intn(len(hc.cfg.Hosts))] path := hc.cfg.Path // TODO: connect use VMess Host instead of H2 Host req := http.Request{ diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 1c09e215..c4f27c4c 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -4,10 +4,11 @@ import ( "bufio" "bytes" "fmt" - "math/rand" "net" "net/http" "net/textproto" + + "github.com/zhangyunhao116/fastrand" ) type httpConn struct { @@ -51,16 +52,16 @@ func (hc *httpConn) Write(b []byte) (int, error) { return hc.Conn.Write(b) } - path := hc.cfg.Path[rand.Intn(len(hc.cfg.Path))] + path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))] host := hc.cfg.Host if header := hc.cfg.Headers["Host"]; len(header) != 0 { - host = header[rand.Intn(len(header))] + host = header[fastrand.Intn(len(header))] } u := fmt.Sprintf("http://%s%s", host, path) req, _ := http.NewRequest("GET", u, bytes.NewBuffer(b)) for key, list := range hc.cfg.Headers { - req.Header.Set(key, list[rand.Intn(len(list))]) + req.Header.Set(key, list[fastrand.Intn(len(list))]) } req.ContentLength = int64(len(b)) if err := req.Write(hc.Conn); err != nil { diff --git a/transport/vmess/vmess.go b/transport/vmess/vmess.go index d7c8edb4..ee7ce121 100644 --- a/transport/vmess/vmess.go +++ b/transport/vmess/vmess.go @@ -2,12 +2,13 @@ package vmess import ( "fmt" - "github.com/Dreamacro/clash/common/utils" - "math/rand" "net" "runtime" + "github.com/Dreamacro/clash/common/utils" + "github.com/gofrs/uuid" + "github.com/zhangyunhao116/fastrand" ) // Version of vmess @@ -77,7 +78,7 @@ type Config struct { // StreamConn return a Conn with net.Conn and DstAddr func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) { - r := rand.Intn(len(c.user)) + r := fastrand.Intn(len(c.user)) return newConn(conn, c.user[r], dst, c.security, c.isAead) } diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index dfadb61a..5fcaa0b8 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -8,9 +8,7 @@ import ( "encoding/binary" "errors" "fmt" - "io" - "math/rand" "net" "net/http" "net/url" @@ -22,7 +20,9 @@ import ( "github.com/Dreamacro/clash/common/buf" N "github.com/Dreamacro/clash/common/net" tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/gorilla/websocket" + "github.com/zhangyunhao116/fastrand" ) type websocketConn struct { @@ -120,7 +120,7 @@ func (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error { binary.BigEndian.PutUint64(header[2:], uint64(dataLen)) } - maskKey := rand.Uint32() + maskKey := fastrand.Uint32() binary.LittleEndian.PutUint32(header[1+payloadBitLength:], maskKey) N.MaskWebSocket(maskKey, data) From 7c34964f878f6c0411067576b80c9492160f0d8c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 6 Mar 2023 19:15:12 +0800 Subject: [PATCH 059/149] fix: dns resolver --- component/resolver/resolver.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 3fd53527..6ae2d7c2 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -69,10 +69,6 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net return r.LookupIPv4(ctx, host) } - if DefaultResolver != nil { - return DefaultResolver.LookupIPv4(ctx, host) - } - ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) if err != nil { return nil, err @@ -126,9 +122,6 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net if r != nil { return r.LookupIPv6(ctx, host) } - if DefaultResolver != nil { - return DefaultResolver.LookupIPv6(ctx, host) - } ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) if err != nil { @@ -172,7 +165,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip } return r.LookupIP(ctx, host) } else if DisableIPv6 { - return LookupIPv4(ctx, host) + return LookupIPv4WithResolver(ctx, host, r) } if ip, err := netip.ParseAddr(host); err == nil { From 545a79d406515809690f6b111388188a3e46501b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 6 Mar 2023 23:23:05 +0800 Subject: [PATCH 060/149] chore: cleanup dialer's code --- component/dialer/dialer.go | 98 +++++++++++++------------------------- hub/executor/executor.go | 6 +-- hub/route/configs.go | 2 +- 3 files changed, 37 insertions(+), 69 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 478e9f19..f53435fb 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -13,6 +13,8 @@ import ( "github.com/Dreamacro/clash/component/resolver" ) +type dialFunc func(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) + var ( dialMux sync.Mutex actualSingleStackDialContext = serialSingleStackDialContext @@ -51,11 +53,16 @@ func DialContext(ctx context.Context, network, address string, options ...Option network = fmt.Sprintf("%s%d", network, opt.network) } + ips, port, err := parseAddr(ctx, network, address, opt.resolver) + if err != nil { + return nil, err + } + switch network { case "tcp4", "tcp6", "udp4", "udp6": - return actualSingleStackDialContext(ctx, network, address, opt) + return actualSingleStackDialContext(ctx, network, ips, port, opt) case "tcp", "udp": - return actualDualStackDialContext(ctx, network, address, opt) + return actualDualStackDialContext(ctx, network, ips, port, opt) default: return nil, ErrorInvalidedNetworkStack } @@ -82,8 +89,9 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio return lc.ListenPacket(ctx, network, address) } -func SetDial(concurrent bool) { +func SetTcpConcurrent(concurrent bool) { dialMux.Lock() + defer dialMux.Unlock() tcpConcurrent = concurrent if concurrent { actualSingleStackDialContext = concurrentSingleStackDialContext @@ -92,11 +100,11 @@ func SetDial(concurrent bool) { actualSingleStackDialContext = serialSingleStackDialContext actualDualStackDialContext = serialDualStackDialContext } - - dialMux.Unlock() } -func GetDial() bool { +func GetTcpConcurrent() bool { + dialMux.Lock() + defer dialMux.Unlock() return tcpConcurrent } @@ -118,71 +126,35 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return dialer.DialContext(ctx, network, address) } -func serialSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { - ips, port, err := parseAddr(ctx, network, address, opt.resolver) - if err != nil { - return nil, err - } +func serialSingleStackDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { return serialDialContext(ctx, network, ips, port, opt) } -func serialDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { - ips, port, err := parseAddr(ctx, network, address, opt.resolver) - if err != nil { - return nil, err - } - ipv4s, ipv6s := sortationAddr(ips) - return dualStackDialContext( - ctx, - func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv4s, port, opt) }, - func(ctx context.Context) (net.Conn, error) { return serialDialContext(ctx, network, ipv6s, port, opt) }, - opt.prefer) +func serialDualStackDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + return dualStackDialContext(ctx, serialDialContext, network, ips, port, opt) } -func concurrentSingleStackDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { - ips, port, err := parseAddr(ctx, network, address, opt.resolver) - if err != nil { - return nil, err - } - - if conn, err := parallelDialContext(ctx, network, ips, port, opt); err != nil { - return nil, err - } else { - return conn, nil - } +func concurrentSingleStackDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + return parallelDialContext(ctx, network, ips, port, opt) } -func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { - ips, port, err := parseAddr(ctx, network, address, opt.resolver) - if err != nil { - return nil, err - } +func concurrentDualStackDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { if opt.prefer != 4 && opt.prefer != 6 { return parallelDialContext(ctx, network, ips, port, opt) } - ipv4s, ipv6s := sortationAddr(ips) - return dualStackDialContext( - ctx, - func(ctx context.Context) (net.Conn, error) { - return parallelDialContext(ctx, network, ipv4s, port, opt) - }, - func(ctx context.Context) (net.Conn, error) { - return parallelDialContext(ctx, network, ipv6s, port, opt) - }, - opt.prefer) + return dualStackDialContext(ctx, parallelDialContext, network, ips, port, opt) } -func dualStackDialContext( - ctx context.Context, - ipv4DialFn func(ctx context.Context) (net.Conn, error), - ipv6DialFn func(ctx context.Context) (net.Conn, error), - preferIPVersion int) (net.Conn, error) { +func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { + ipv4s, ipv6s := sortationAddr(ips) + preferIPVersion := opt.prefer + fallbackTicker := time.NewTicker(fallbackTimeout) defer fallbackTicker.Stop() results := make(chan dialResult) returned := make(chan struct{}) defer close(returned) - racer := func(dial func(ctx context.Context) (net.Conn, error), isPrimary bool) { + racer := func(ips []netip.Addr, isPrimary bool) { result := dialResult{isPrimary: isPrimary} defer func() { select { @@ -193,10 +165,10 @@ func dualStackDialContext( } } }() - result.Conn, result.error = dial(ctx) + result.Conn, result.error = dialFn(ctx, network, ips, port, opt) } - go racer(ipv4DialFn, preferIPVersion != 6) - go racer(ipv6DialFn, preferIPVersion != 4) + go racer(ipv4s, preferIPVersion != 6) + go racer(ipv6s, preferIPVersion != 4) var fallback dialResult var err error for { @@ -230,7 +202,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, results := make(chan dialResult) returned := make(chan struct{}) defer close(returned) - tcpRacer := func(ctx context.Context, ip netip.Addr, port string) { + racer := func(ctx context.Context, ip netip.Addr) { result := dialResult{isPrimary: true} defer func() { select { @@ -246,7 +218,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, } for _, ip := range ips { - go tcpRacer(ctx, ip, port) + go racer(ctx, ip) } var err error for { @@ -272,13 +244,9 @@ func serialDialContext(ctx context.Context, network string, ips []netip.Addr, po if len(ips) == 0 { return nil, ErrorNoIpAddress } - var ( - conn net.Conn - err error - errs []error - ) + var errs []error for _, ip := range ips { - if conn, err = dialContext(ctx, network, ip, port, opt); err == nil { + if conn, err := dialContext(ctx, network, ip, port, opt); err == nil { return conn, nil } else { errs = append(errs, err) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index c55201cd..d6ff7851 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -128,7 +128,7 @@ func GetGeneral() *config.General { GeodataLoader: G.LoaderName(), Interface: dialer.DefaultInterface.Load(), Sniffing: tunnel.IsSniffing(), - TCPConcurrent: dialer.GetDial(), + TCPConcurrent: dialer.GetTcpConcurrent(), } return general @@ -331,10 +331,10 @@ func updateTunnels(tunnels []LC.Tunnel) { func updateGeneral(general *config.General) { tunnel.SetMode(general.Mode) tunnel.SetFindProcessMode(general.FindProcessMode) - resolver.DisableIPv6 =!general.IPv6 + resolver.DisableIPv6 = !general.IPv6 if general.TCPConcurrent { - dialer.SetDial(general.TCPConcurrent) + dialer.SetTcpConcurrent(general.TCPConcurrent) log.Infoln("Use tcp concurrent") } diff --git a/hub/route/configs.go b/hub/route/configs.go index 9e630b29..50e3cd13 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -228,7 +228,7 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { } if general.TcpConcurrent != nil { - dialer.SetDial(*general.TcpConcurrent) + dialer.SetTcpConcurrent(*general.TcpConcurrent) } if general.InterfaceName != nil { From 9cc7fdaca9009d00abb41ee38f4eca0d70b6c45f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 7 Mar 2023 09:30:51 +0800 Subject: [PATCH 061/149] chore: wireguard using internal dialer --- adapter/outbound/wireguard.go | 42 ++++++++++++++++++++++------------- component/dialer/dialer.go | 24 ++++++++++++++------ component/dialer/options.go | 14 ++++++++++++ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index e3dafbbf..e5d7cf3f 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -34,7 +34,7 @@ type WireGuard struct { bind *wireguard.ClientBind device *device.Device tunDevice wireguard.Device - dialer *wgDialer + dialer *wgSingDialer startOnce sync.Once startErr error } @@ -56,16 +56,28 @@ type WireGuardOption struct { PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` } -type wgDialer struct { - options []dialer.Option +type wgSingDialer struct { + dialer dialer.Dialer } -func (d *wgDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return dialer.DialContext(ctx, network, destination.String(), d.options...) +var _ N.Dialer = &wgSingDialer{} + +func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return d.dialer.DialContext(ctx, network, destination.String()) } -func (d *wgDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", destination.Addr), "", d.options...) +func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return d.dialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) +} + +type wgNetDialer struct { + tunDevice wireguard.Device +} + +var _ dialer.NetDialer = &wgNetDialer{} + +func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address)) } func NewWireGuard(option WireGuardOption) (*WireGuard, error) { @@ -79,7 +91,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, - dialer: &wgDialer{}, + dialer: &wgSingDialer{dialer: dialer.NewDialer()}, } runtime.SetFinalizer(outbound, closeWireGuard) @@ -199,7 +211,8 @@ func closeWireGuard(w *WireGuard) { } func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - w.dialer.options = opts + options := w.Base.DialOptions(opts...) + w.dialer.dialer = dialer.NewDialer(options...) var conn net.Conn w.startOnce.Do(func() { w.startErr = w.tunDevice.Start() @@ -208,12 +221,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts return nil, w.startErr } if !metadata.Resolved() { - var addrs []netip.Addr - addrs, err = resolver.LookupIP(ctx, metadata.Host) - if err != nil { - return nil, err - } - conn, err = N.DialSerial(ctx, w.tunDevice, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()), addrs) + options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice})) + conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress()) } else { port, _ := strconv.Atoi(metadata.DstPort) conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, uint16(port))) @@ -228,7 +237,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts } func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { - w.dialer.options = opts + options := w.Base.DialOptions(opts...) + w.dialer.dialer = dialer.NewDialer(options...) var pc net.PacketConn w.startOnce.Do(func() { w.startErr = w.tunDevice.Start() diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index f53435fb..025f7034 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -109,7 +109,19 @@ func GetTcpConcurrent() bool { } func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) { - dialer := &net.Dialer{} + address := net.JoinHostPort(destination.String(), port) + + netDialer := opt.netDialer + switch netDialer.(type) { + case nil: + netDialer = &net.Dialer{} + case *net.Dialer: + netDialer = &*netDialer.(*net.Dialer) // make a copy + default: + return netDialer.DialContext(ctx, network, address) + } + + dialer := netDialer.(*net.Dialer) if opt.interfaceName != "" { if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil { return nil, err @@ -118,8 +130,6 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po if opt.routingMark != 0 { bindMarkToDialer(opt.routingMark, dialer, network, destination) } - - address := net.JoinHostPort(destination.String(), port) if opt.tfo { return dialTFO(ctx, *dialer, network, address) } @@ -307,15 +317,15 @@ func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { } type Dialer struct { - Opt option + opt option } func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - return DialContext(ctx, network, address, WithOption(d.Opt)) + return DialContext(ctx, network, address, WithOption(d.opt)) } func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - opt := WithOption(d.Opt) + opt := WithOption(d.opt) if rAddrPort.Addr().Unmap().IsLoopback() { // avoid "The requested address is not valid in its context." opt = WithInterface("") @@ -325,5 +335,5 @@ func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddr func NewDialer(options ...Option) Dialer { opt := applyOptions(options...) - return Dialer{Opt: *opt} + return Dialer{opt: *opt} } diff --git a/component/dialer/options.go b/component/dialer/options.go index 1c4e7bfc..372a2e63 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -1,6 +1,9 @@ package dialer import ( + "context" + "net" + "github.com/Dreamacro/clash/component/resolver" "go.uber.org/atomic" @@ -12,6 +15,10 @@ var ( DefaultRoutingMark = atomic.NewInt32(0) ) +type NetDialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) +} + type option struct { interfaceName string addrReuse bool @@ -20,6 +27,7 @@ type option struct { prefer int tfo bool resolver resolver.Resolver + netDialer NetDialer } type Option func(opt *option) @@ -76,6 +84,12 @@ func WithTFO(tfo bool) Option { } } +func WithNetDialer(netDialer NetDialer) Option { + return func(opt *option) { + opt.netDialer = netDialer + } +} + func WithOption(o option) Option { return func(opt *option) { *opt = o From a0ad12c45b04c1fbceaeb3e5a9db8240ce8a6d53 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 7 Mar 2023 14:11:38 +0800 Subject: [PATCH 062/149] =?UTF-8?q?fix:=20sing-vmess=20listener=E2=80=98s?= =?UTF-8?q?=20"cipher:=20message=20authentication=20failed"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1444da40..f8a2dc19 100644 --- a/go.mod +++ b/go.mod @@ -26,9 +26,9 @@ require ( 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.20230303052048-c875a4ffab1a + github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b github.com/sagernet/sing-shadowtls v0.1.0 - github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b + github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc 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 7774aa64..d68126bf 100644 --- a/go.sum +++ b/go.sum @@ -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.20230303052048-c875a4ffab1a h1:NvhI/8DMFt2yV3eoYhw6P/XyWzzIKkMiGvFglJbWHWg= -github.com/sagernet/sing v0.1.8-0.20230303052048-c875a4ffab1a/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b h1:wxqf3O+cLHm1ZWEQG1DRwApwLlTV/NLKGqF1kNCk3Ms= +github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= -github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b h1:NZeF0ATeJwe4W3gTJNeIfTB6yBxai665q1HvDOaWmmU= -github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY= +github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvVDRpvv2F+RtECJN+VbuKjLtmQculQvOecls= +github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc/go.mod h1:V14iffGwhZPU2S7wgIiPlLWXygSjAXazYzD1w0ejBl4= 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= From 04ae812a1179433b8579ffde29b2ee6adaf75040 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:52:50 +0800 Subject: [PATCH 063/149] chore: try to fix slice out of bound. --- transport/vless/vision.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/transport/vless/vision.go b/transport/vless/vision.go index d817c912..b80fbb71 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -3,6 +3,8 @@ package vless import ( "bytes" "encoding/binary" + "sync" + "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/log" @@ -18,18 +20,19 @@ const ( commandPaddingDirect byte = 0x02 ) +var mutex sync.RWMutex + func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(len(p)) + mutex.Lock() var paddingLen int32 if contentLen < 900 && paddingTLS { - log.Debugln("long padding") + //log.Debugln("long padding") paddingLen = fastrand.Int31n(500) + 900 - contentLen } else { paddingLen = fastrand.Int31n(256) } - if paddingLen > buf.BufferSize-21-contentLen { - paddingLen = buf.BufferSize - 21 - contentLen - } + mutex.Unlock() if userUUID != nil { // unnecessary, but keep the same with Xray buffer.Write(userUUID.Bytes()) } @@ -38,28 +41,30 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid binary.BigEndian.PutUint16(buffer.Extend(2), uint16(contentLen)) binary.BigEndian.PutUint16(buffer.Extend(2), uint16(paddingLen)) buffer.Write(p) + buffer.Extend(int(paddingLen)) log.Debugln("XTLS Vision write padding1: command=%v, payloadLen=%v, paddingLen=%v", command, contentLen, paddingLen) } func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(buffer.Len()) + mutex.Lock() var paddingLen int32 if contentLen < 900 && paddingTLS { - log.Debugln("long padding") + //log.Debugln("long padding") paddingLen = fastrand.Int31n(500) + 900 - contentLen } else { paddingLen = fastrand.Int31n(256) } - if paddingLen > buf.BufferSize-21-contentLen { - paddingLen = buf.BufferSize - 21 - contentLen - } + mutex.Unlock() + binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen)) binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen)) buffer.ExtendHeader(1)[0] = command if userUUID != nil { // unnecessary, but keep the same with Xray copy(buffer.ExtendHeader(uuid.Size), userUUID.Bytes()) } + buffer.Extend(int(paddingLen)) log.Debugln("XTLS Vision write padding2: command=%d, payloadLen=%d, paddingLen=%d", command, contentLen, paddingLen) } From 6040803b60a0207c292fa3eb8d0b9cdb1ceac13a Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 7 Mar 2023 16:34:57 +0800 Subject: [PATCH 064/149] chore: do not apply padding for nonTLS packet with contentLen over 900 --- transport/vless/vision.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/transport/vless/vision.go b/transport/vless/vision.go index b80fbb71..857d9150 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -24,13 +24,15 @@ var mutex sync.RWMutex func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(len(p)) - mutex.Lock() var paddingLen int32 - if contentLen < 900 && paddingTLS { - //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen - } else { - paddingLen = fastrand.Int31n(256) + mutex.Lock() + if contentLen < 900 { + if paddingTLS { + //log.Debugln("long padding") + paddingLen = fastrand.Int31n(500) + 900 - contentLen + } else { + paddingLen = fastrand.Int31n(256) + } } mutex.Unlock() if userUUID != nil { // unnecessary, but keep the same with Xray @@ -48,13 +50,15 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(buffer.Len()) - mutex.Lock() var paddingLen int32 - if contentLen < 900 && paddingTLS { - //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen - } else { - paddingLen = fastrand.Int31n(256) + mutex.Lock() + if contentLen < 900 { + if paddingTLS { + //log.Debugln("long padding") + paddingLen = fastrand.Int31n(500) + 900 - contentLen + } else { + paddingLen = fastrand.Int31n(256) + } } mutex.Unlock() From 1e6f0f28f64f15cc56ffbb0194e7b8746285508f Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 8 Mar 2023 00:19:20 +0800 Subject: [PATCH 065/149] chore: change default geo* url --- config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index 76e5491b..efb5b4cf 100644 --- a/config/config.go +++ b/config/config.go @@ -415,9 +415,9 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { StoreSelected: true, }, GeoXUrl: RawGeoXUrl{ - GeoIp: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat", - Mmdb: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb", - GeoSite: "https://ghproxy.com/https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geosite.dat", + Mmdb: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb", + GeoIp: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat", + GeoSite: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat", }, } From 76a8fe38394457611d337d546cab5d6fe9606b22 Mon Sep 17 00:00:00 2001 From: H1JK Date: Wed, 8 Mar 2023 17:18:46 +0800 Subject: [PATCH 066/149] feat: Support REALITY protocol --- adapter/outbound/reality.go | 41 +++++++++ adapter/outbound/trojan.go | 37 ++++---- adapter/outbound/vless.go | 9 +- adapter/outbound/vmess.go | 59 +++++++------ component/tls/reality.go | 163 ++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- transport/trojan/trojan.go | 22 +++-- transport/vmess/tls.go | 24 ++++-- 9 files changed, 305 insertions(+), 56 deletions(-) create mode 100644 adapter/outbound/reality.go create mode 100644 component/tls/reality.go diff --git a/adapter/outbound/reality.go b/adapter/outbound/reality.go new file mode 100644 index 00000000..05f49d11 --- /dev/null +++ b/adapter/outbound/reality.go @@ -0,0 +1,41 @@ +package outbound + +import ( + "encoding/base64" + "encoding/hex" + "errors" + + tlsC "github.com/Dreamacro/clash/component/tls" + + "golang.org/x/crypto/curve25519" +) + +type RealityOptions struct { + ServerName string `proxy:"server-name"` + PublicKey string `proxy:"public-key"` + ShortID string `proxy:"short-id"` +} + +func (o RealityOptions) Parse() (*tlsC.RealityConfig, error) { + if o.PublicKey != "" || o.ServerName != "" { + if o.PublicKey != "" && o.ServerName != "" { + config := new(tlsC.RealityConfig) + + n, err := base64.RawURLEncoding.Decode(config.PublicKey[:], []byte(o.PublicKey)) + if err != nil || n != curve25519.ScalarSize { + return nil, errors.New("invalid REALITY public key") + } + + config.ShortID, err = hex.DecodeString(o.ShortID) + if err != nil { + return nil, errors.New("invalid REALITY short ID") + } + + config.ServerName = o.ServerName + + return config, nil + } + return nil, errors.New("invalid REALITY protocol option") + } + return nil, nil +} diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index beedd614..d36bd40c 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -30,21 +30,22 @@ type Trojan struct { type TrojanOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Password string `proxy:"password"` - ALPN []string `proxy:"alpn,omitempty"` - SNI string `proxy:"sni,omitempty"` - SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` - Fingerprint string `proxy:"fingerprint,omitempty"` - UDP bool `proxy:"udp,omitempty"` - Network string `proxy:"network,omitempty"` - GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` - WSOpts WSOptions `proxy:"ws-opts,omitempty"` - Flow string `proxy:"flow,omitempty"` - FlowShow bool `proxy:"flow-show,omitempty"` - ClientFingerprint string `proxy:"client-fingerprint,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Password string `proxy:"password"` + ALPN []string `proxy:"alpn,omitempty"` + SNI string `proxy:"sni,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + UDP bool `proxy:"udp,omitempty"` + Network string `proxy:"network,omitempty"` + RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` + GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` + WSOpts WSOptions `proxy:"ws-opts,omitempty"` + Flow string `proxy:"flow,omitempty"` + FlowShow bool `proxy:"flow-show,omitempty"` + ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) { @@ -244,6 +245,12 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { tOption.ServerName = option.SNI } + var err error + tOption.Reality, err = option.RealityOpts.Parse() + if err != nil { + return nil, err + } + t := &Trojan{ Base: &Base{ name: option.Name, diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 010af23c..435beee9 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -41,6 +41,8 @@ type Vless struct { gunTLSConfig *tls.Config gunConfig *gun.Config transport *gun.TransportWrap + + realityConfig *tlsC.RealityConfig } type VlessOption struct { @@ -57,6 +59,7 @@ type VlessOption struct { XUDP bool `proxy:"xudp,omitempty"` PacketEncoding string `proxy:"packet-encoding,omitempty"` Network string `proxy:"network,omitempty"` + RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` @@ -78,7 +81,6 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { switch v.option.Network { case "ws": - host, port, _ := net.SplitHostPort(v.addr) wsOpts := &vmess.WebsocketConfig{ Host: host, @@ -190,6 +192,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) SkipCertVerify: v.option.SkipCertVerify, FingerPrint: v.option.Fingerprint, ClientFingerprint: v.option.ClientFingerprint, + Reality: v.realityConfig, } if isH2 { @@ -554,7 +557,11 @@ func NewVless(option VlessOption) (*Vless, error) { v.gunConfig = gunConfig v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint) + } + v.realityConfig, err = v.option.RealityOpts.Parse() + if err != nil { + return nil, err } return v, nil diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 25ffebf3..f9e7205b 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -35,32 +35,35 @@ type Vmess struct { gunTLSConfig *tls.Config gunConfig *gun.Config transport *gun.TransportWrap + + realityConfig *tlsC.RealityConfig } type VmessOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - UUID string `proxy:"uuid"` - AlterID int `proxy:"alterId"` - Cipher string `proxy:"cipher"` - UDP bool `proxy:"udp,omitempty"` - Network string `proxy:"network,omitempty"` - TLS bool `proxy:"tls,omitempty"` - SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` - Fingerprint string `proxy:"fingerprint,omitempty"` - ServerName string `proxy:"servername,omitempty"` - HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` - HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` - GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` - WSOpts WSOptions `proxy:"ws-opts,omitempty"` - PacketAddr bool `proxy:"packet-addr,omitempty"` - XUDP bool `proxy:"xudp,omitempty"` - PacketEncoding string `proxy:"packet-encoding,omitempty"` - GlobalPadding bool `proxy:"global-padding,omitempty"` - AuthenticatedLength bool `proxy:"authenticated-length,omitempty"` - ClientFingerprint string `proxy:"client-fingerprint,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + UUID string `proxy:"uuid"` + AlterID int `proxy:"alterId"` + Cipher string `proxy:"cipher"` + UDP bool `proxy:"udp,omitempty"` + Network string `proxy:"network,omitempty"` + TLS bool `proxy:"tls,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + ServerName string `proxy:"servername,omitempty"` + RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` + HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` + HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` + GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` + WSOpts WSOptions `proxy:"ws-opts,omitempty"` + PacketAddr bool `proxy:"packet-addr,omitempty"` + XUDP bool `proxy:"xudp,omitempty"` + PacketEncoding string `proxy:"packet-encoding,omitempty"` + GlobalPadding bool `proxy:"global-padding,omitempty"` + AuthenticatedLength bool `proxy:"authenticated-length,omitempty"` + ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } type HTTPOptions struct { @@ -95,7 +98,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { switch v.option.Network { case "ws": - host, port, _ := net.SplitHostPort(v.addr) wsOpts := &clashVMess.WebsocketConfig{ Host: host, @@ -144,12 +146,12 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { Host: host, SkipCertVerify: v.option.SkipCertVerify, ClientFingerprint: v.option.ClientFingerprint, + Reality: v.realityConfig, } if v.option.ServerName != "" { tlsOpts.Host = v.option.ServerName } - c, err = clashVMess.StreamTLSConn(c, tlsOpts) if err != nil { return nil, err @@ -172,6 +174,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { SkipCertVerify: v.option.SkipCertVerify, NextProtos: []string{"h2"}, ClientFingerprint: v.option.ClientFingerprint, + Reality: v.realityConfig, } if v.option.ServerName != "" { @@ -199,6 +202,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { Host: host, SkipCertVerify: v.option.SkipCertVerify, ClientFingerprint: v.option.ClientFingerprint, + Reality: v.realityConfig, } if v.option.ServerName != "" { @@ -452,8 +456,13 @@ func NewVmess(option VmessOption) (*Vmess, error) { v.gunConfig = gunConfig v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint) - } + + v.realityConfig, err = v.option.RealityOpts.Parse() + if err != nil { + return nil, err + } + return v, nil } diff --git a/component/tls/reality.go b/component/tls/reality.go new file mode 100644 index 00000000..cdad690f --- /dev/null +++ b/component/tls/reality.go @@ -0,0 +1,163 @@ +package tls + +import ( + "bytes" + "context" + "crypto/aes" + "crypto/cipher" + "crypto/ed25519" + "crypto/hmac" + "crypto/sha256" + "crypto/sha512" + "crypto/tls" + "crypto/x509" + "encoding/binary" + "errors" + "net" + "net/http" + "reflect" + "strings" + "time" + "unsafe" + + "github.com/Dreamacro/clash/log" + + utls "github.com/sagernet/utls" + "github.com/zhangyunhao116/fastrand" + "golang.org/x/crypto/curve25519" + "golang.org/x/crypto/hkdf" + "golang.org/x/net/http2" +) + +type RealityConfig struct { + ServerName string + PublicKey [curve25519.ScalarSize]byte + ShortID []byte +} + +func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) { + if fingerprint, exists := GetFingerprint(ClientFingerprint); exists { + verifier := &realityVerifier{ + serverName: realityConfig.ServerName, + } + uConfig := copyConfig(tlsConfig) + uConfig.ServerName = realityConfig.ServerName + uConfig.InsecureSkipVerify = true + uConfig.SessionTicketsDisabled = true + uConfig.VerifyPeerCertificate = verifier.VerifyPeerCertificate + clientID := utls.ClientHelloID{ + Client: fingerprint.Client, + Version: fingerprint.Version, + Seed: fingerprint.Seed, + } + uConn := utls.UClient(conn, uConfig, clientID) + verifier.UConn = uConn + err := uConn.BuildHandshakeState() + if err != nil { + return nil, err + } + + hello := uConn.HandshakeState.Hello + hello.SessionId = make([]byte, 32) + copy(hello.Raw[39:], hello.SessionId) + + var nowTime time.Time + if uConfig.Time != nil { + nowTime = uConfig.Time() + } else { + nowTime = time.Now() + } + binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix())) + + hello.SessionId[0] = 1 + hello.SessionId[1] = 7 + hello.SessionId[2] = 5 + copy(hello.SessionId[8:], realityConfig.ShortID) + + //log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16]) + + authKey := uConn.HandshakeState.State13.EcdheParams.SharedKey(realityConfig.PublicKey[:]) + if authKey == nil { + return nil, errors.New("nil auth_key") + } + verifier.authKey = authKey + _, err = hkdf.New(sha256.New, authKey, hello.Random[:20], []byte("REALITY")).Read(authKey) + if err != nil { + return nil, err + } + aesBlock, _ := aes.NewCipher(authKey) + aesGcmCipher, _ := cipher.NewGCM(aesBlock) + aesGcmCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw) + copy(hello.Raw[39:], hello.SessionId) + //log.Debugln("REALITY hello.sessionId: %v", hello.SessionId) + //log.Debugln("REALITY uConn.AuthKey: %v", authKey) + + err = uConn.HandshakeContext(ctx) + if err != nil { + return nil, err + } + + log.Debugln("REALITY Authentication: %v", verifier.verified) + + if !verifier.verified { + go realityClientFallback(uConn, uConfig.ServerName, clientID) + return nil, errors.New("REALITY authentication failed") + } + + return uConn, nil + } + return nil, errors.New("unknown uTLS fingerprint") +} + +func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) { + defer uConn.Close() + client := &http.Client{ + Transport: &http2.Transport{ + DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) { + return uConn, nil + }, + }, + } + request, _ := http.NewRequest("GET", "https://"+serverName, nil) + request.Header.Set("User-Agent", fingerprint.Client) + request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)}) + response, err := client.Do(request) + if err != nil { + return + } + //_, _ = io.Copy(io.Discard, response.Body) + time.Sleep(time.Duration(5 + fastrand.Int63n(10))) + response.Body.Close() + client.CloseIdleConnections() +} + +type realityVerifier struct { + *utls.UConn + serverName string + authKey []byte + verified bool +} + +func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") + certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset)) + if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok { + h := hmac.New(sha512.New, c.authKey) + h.Write(pub) + if bytes.Equal(h.Sum(nil), certs[0].Signature) { + c.verified = true + return nil + } + } + opts := x509.VerifyOptions{ + DNSName: c.serverName, + Intermediates: x509.NewCertPool(), + } + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + if _, err := certs[0].Verify(opts); err != nil { + return err + } + return nil +} diff --git a/go.mod b/go.mod index f8a2dc19..21287160 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d - github.com/sagernet/utls v0.0.0-20230220130002-c08891932056 + github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index d68126bf..8c100900 100644 --- a/go.sum +++ b/go.sum @@ -135,8 +135,8 @@ github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvV github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc/go.mod h1:V14iffGwhZPU2S7wgIiPlLWXygSjAXazYzD1w0ejBl4= 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= -github.com/sagernet/utls v0.0.0-20230220130002-c08891932056/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= +github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 h1:m4MI13+NRKddIvbdSN0sFHK8w5ROTa60Zi9diZ7EE08= +github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index e336d9db..8eae8237 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -19,6 +19,7 @@ import ( "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vmess" + xtls "github.com/xtls/go" ) @@ -54,6 +55,7 @@ type Option struct { Flow string FlowShow bool ClientFingerprint string + Reality *tlsC.RealityConfig } type WebsocketOption struct { @@ -117,16 +119,24 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) { } if len(t.option.ClientFingerprint) != 0 { - utlsConn, valid := vmess.GetUtlsConnWithClientFingerprint(conn, t.option.ClientFingerprint, tlsConfig) - if valid { + if t.option.Reality == nil { + utlsConn, valid := vmess.GetUTLSConn(conn, t.option.ClientFingerprint, tlsConfig) + if valid { + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + + err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx) + return utlsConn, err + } + } else { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) defer cancel() - - err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx) - return utlsConn, err - + return tlsC.GetRealityConn(ctx, conn, t.option.ClientFingerprint, tlsConfig, t.option.Reality) } } + if t.option.Reality != nil { + return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint") + } tlsConn := tls.Client(conn, tlsConfig) diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index 711c342d..f020d273 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -3,6 +3,7 @@ package vmess import ( "context" "crypto/tls" + "errors" "net" tlsC "github.com/Dreamacro/clash/component/tls" @@ -15,6 +16,7 @@ type TLSConfig struct { FingerPrint string ClientFingerprint string NextProtos []string + Reality *tlsC.RealityConfig } func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { @@ -34,15 +36,25 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { } if len(cfg.ClientFingerprint) != 0 { - utlsConn, valid := GetUtlsConnWithClientFingerprint(conn, cfg.ClientFingerprint, tlsConfig) - if valid { + if cfg.Reality == nil { + utlsConn, valid := GetUTLSConn(conn, cfg.ClientFingerprint, tlsConfig) + if valid { + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + + err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx) + return utlsConn, err + } + } else { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) defer cancel() - - err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx) - return utlsConn, err + return tlsC.GetRealityConn(ctx, conn, cfg.ClientFingerprint, tlsConfig, cfg.Reality) } } + if cfg.Reality != nil { + return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint") + } + tlsConn := tls.Client(conn, tlsConfig) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) @@ -52,7 +64,7 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { return tlsConn, err } -func GetUtlsConnWithClientFingerprint(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (net.Conn, bool) { +func GetUTLSConn(conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config) (net.Conn, bool) { if fingerprint, exists := tlsC.GetFingerprint(ClientFingerprint); exists { utlsConn := tlsC.UClient(conn, tlsConfig, fingerprint) From 8ba7ce73d8cf37bca9b39c160955c6d6afe45d34 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 8 Mar 2023 19:12:51 +0800 Subject: [PATCH 067/149] Update config.yaml --- docs/config.yaml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index 13c5a83c..03b35cc8 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -436,11 +436,29 @@ proxies: # socks5 tls: true udp: true xudp: true - flow: xtls-rprx-vision # xtls-rprx-origin # enable XTLS + flow: xtls-rprx-vision client-fingerprint: chrome # fingerprint: xxxx # skip-cert-verify: true - + + - name: "vless-reality-vision" + type: vless + server: server + port: 443 + uuid: uuid + network: tcp + tls: true + udp: true + xudp: true + flow: xtls-rprx-vision + reality-opts: + server-name: www.microsoft.com + public-key: xxx + short-id: xxx + client-fingerprint: chrome + # fingerprint: xxxx + # skip-cert-verify: true + - name: "vless-ws" type: vless server: server @@ -874,4 +892,4 @@ listeners: # authentication-timeout: 1000 # alpn: # - h3 -# max-udp-relay-packet-size: 1500 \ No newline at end of file +# max-udp-relay-packet-size: 1500 From 921b2c3aa4157ec7d02b7baa0ec7c6d4cb49b59d Mon Sep 17 00:00:00 2001 From: H1JK Date: Wed, 8 Mar 2023 20:28:12 +0800 Subject: [PATCH 068/149] feat: REALITY use proxy servername --- adapter/outbound/reality.go | 34 ++++++++++++++-------------------- component/tls/reality.go | 18 +++++++++--------- docs/config.yaml | 8 +++----- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/adapter/outbound/reality.go b/adapter/outbound/reality.go index 05f49d11..9d892f96 100644 --- a/adapter/outbound/reality.go +++ b/adapter/outbound/reality.go @@ -11,31 +11,25 @@ import ( ) type RealityOptions struct { - ServerName string `proxy:"server-name"` - PublicKey string `proxy:"public-key"` - ShortID string `proxy:"short-id"` + PublicKey string `proxy:"public-key"` + ShortID string `proxy:"short-id"` } func (o RealityOptions) Parse() (*tlsC.RealityConfig, error) { - if o.PublicKey != "" || o.ServerName != "" { - if o.PublicKey != "" && o.ServerName != "" { - config := new(tlsC.RealityConfig) + if o.PublicKey != "" { + config := new(tlsC.RealityConfig) - n, err := base64.RawURLEncoding.Decode(config.PublicKey[:], []byte(o.PublicKey)) - if err != nil || n != curve25519.ScalarSize { - return nil, errors.New("invalid REALITY public key") - } - - config.ShortID, err = hex.DecodeString(o.ShortID) - if err != nil { - return nil, errors.New("invalid REALITY short ID") - } - - config.ServerName = o.ServerName - - return config, nil + n, err := base64.RawURLEncoding.Decode(config.PublicKey[:], []byte(o.PublicKey)) + if err != nil || n != curve25519.ScalarSize { + return nil, errors.New("invalid REALITY public key") } - return nil, errors.New("invalid REALITY protocol option") + + config.ShortID, err = hex.DecodeString(o.ShortID) + if err != nil { + return nil, errors.New("invalid REALITY short ID") + } + + return config, nil } return nil, nil } diff --git a/component/tls/reality.go b/component/tls/reality.go index cdad690f..732613d8 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -30,21 +30,21 @@ import ( ) type RealityConfig struct { - ServerName string - PublicKey [curve25519.ScalarSize]byte - ShortID []byte + PublicKey [curve25519.ScalarSize]byte + ShortID []byte } func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) { if fingerprint, exists := GetFingerprint(ClientFingerprint); exists { verifier := &realityVerifier{ - serverName: realityConfig.ServerName, + serverName: tlsConfig.ServerName, + } + uConfig := &utls.Config{ + ServerName: tlsConfig.ServerName, + InsecureSkipVerify: true, + SessionTicketsDisabled: true, + VerifyPeerCertificate: verifier.VerifyPeerCertificate, } - uConfig := copyConfig(tlsConfig) - uConfig.ServerName = realityConfig.ServerName - uConfig.InsecureSkipVerify = true - uConfig.SessionTicketsDisabled = true - uConfig.VerifyPeerCertificate = verifier.VerifyPeerCertificate clientID := utls.ClientHelloID{ Client: fingerprint.Client, Version: fingerprint.Version, diff --git a/docs/config.yaml b/docs/config.yaml index 03b35cc8..971ca124 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -451,13 +451,11 @@ proxies: # socks5 udp: true xudp: true flow: xtls-rprx-vision + servername: www.microsoft.com # REALITY servername reality-opts: - server-name: www.microsoft.com public-key: xxx - short-id: xxx - client-fingerprint: chrome - # fingerprint: xxxx - # skip-cert-verify: true + short-id: xxx # optional + client-fingerprint: chrome # cannot be empty - name: "vless-ws" type: vless From 0c984644b46de51876f809d2211bb60448fed503 Mon Sep 17 00:00:00 2001 From: metacubex Date: Thu, 9 Mar 2023 01:31:43 +0800 Subject: [PATCH 069/149] chore: parse the allowInsecure field for the trojan uri scheme --- common/convert/converter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/convert/converter.go b/common/convert/converter.go index 7d896d53..abd07a94 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -83,7 +83,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { trojan["port"] = urlTrojan.Port() trojan["password"] = urlTrojan.User.Username() trojan["udp"] = true - trojan["skip-cert-verify"] = false + trojan["skip-cert-verify"], _ = strconv.ParseBool(query.Get("allowInsecure")) sni := query.Get("sni") if sni != "" { From bae61a81522adbde70086366a4e3c84c9554677e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 9 Mar 2023 10:41:07 +0800 Subject: [PATCH 070/149] fix: tuic server close with error message --- transport/tuic/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/tuic/server.go b/transport/tuic/server.go index e8dee8d6..fb8a30d3 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -89,7 +89,7 @@ type serverHandler struct { func (s *serverHandler) handle() { time.AfterFunc(s.AuthenticationTimeout, func() { s.authOnce.Do(func() { - _ = s.quicConn.CloseWithError(AuthenticationTimeout, "") + _ = s.quicConn.CloseWithError(AuthenticationTimeout, "AuthenticationTimeout") s.authOk = false close(s.authCh) }) @@ -239,7 +239,7 @@ func (s *serverHandler) handleUniStream() (err error) { } s.authOnce.Do(func() { if !ok { - _ = s.quicConn.CloseWithError(AuthenticationFailed, "") + _ = s.quicConn.CloseWithError(AuthenticationFailed, "AuthenticationFailed") } s.authOk = ok close(s.authCh) From a454a7f736500364e8c985f473502fa48db6b585 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 9 Mar 2023 11:09:36 +0800 Subject: [PATCH 071/149] fix: load-balance's touch not effected --- adapter/outboundgroup/loadbalance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 7c7edd36..703444da 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -216,7 +216,7 @@ func strategyStickySessions() strategyFn { // Unwrap implements C.ProxyAdapter func (lb *LoadBalance) Unwrap(metadata *C.Metadata, touch bool) C.Proxy { proxies := lb.GetProxies(touch) - return lb.strategyFn(proxies, metadata, true) + return lb.strategyFn(proxies, metadata, touch) } // MarshalJSON implements C.ProxyAdapter From a973a6c7d2e8f2ccc787d8604956699db48ebf56 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 9 Mar 2023 12:33:29 +0800 Subject: [PATCH 072/149] chore: update utls library --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 21287160..26e47b8c 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d - github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 + github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 8c100900..49666074 100644 --- a/go.sum +++ b/go.sum @@ -135,8 +135,8 @@ github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvV github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc/go.mod h1:V14iffGwhZPU2S7wgIiPlLWXygSjAXazYzD1w0ejBl4= 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-20230225061716-536a007c8b01 h1:m4MI13+NRKddIvbdSN0sFHK8w5ROTa60Zi9diZ7EE08= -github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= +github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= +github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= From c0fc5d142f30dba9b4968e4a3ac989c34c6ff31f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 00:25:22 +0800 Subject: [PATCH 073/149] fix: unmap 4in6 address in dialer and wireguard --- adapter/outbound/wireguard.go | 6 +++--- component/dialer/dialer.go | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index e5d7cf3f..51c9ecb9 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -77,7 +77,7 @@ type wgNetDialer struct { var _ dialer.NetDialer = &wgNetDialer{} func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address)) + return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap()) } func NewWireGuard(option WireGuardOption) (*WireGuard, error) { @@ -225,7 +225,7 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress()) } else { port, _ := strconv.Atoi(metadata.DstPort) - conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, uint16(port))) + conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, uint16(port)).Unwrap()) } if err != nil { return nil, err @@ -257,7 +257,7 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat metadata.DstIP = ip } port, _ := strconv.Atoi(metadata.DstPort) - pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, uint16(port))) + pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, uint16(port)).Unwrap()) if err != nil { return nil, err } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 025f7034..3014b812 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -302,13 +302,18 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso if err != nil { return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) } + for i, ip := range ips { + if ip.Is4In6() { + ips[i] = ip.Unmap() + } + } return ips, port, nil } func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { for _, v := range ips { - if v.Is4() || v.Is4In6() { - ipv4s = append(ipv4s, v.Unmap()) + if v.Is4() { // 4in6 parse was in parseAddr + ipv4s = append(ipv4s, v) } else { ipv6s = append(ipv6s, v) } From dca98b7aa1b73b5b5c33b6db89266ce0dbd7b5ad Mon Sep 17 00:00:00 2001 From: H1JK Date: Fri, 10 Mar 2023 10:01:05 +0800 Subject: [PATCH 074/149] fix: REALITY with gRPC transport --- adapter/outbound/trojan.go | 19 +++++++++-------- adapter/outbound/vless.go | 14 ++++++------- adapter/outbound/vmess.go | 4 ++-- transport/gun/gun.go | 42 +++++++++++++++++++++++++++----------- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index d36bd40c..030e74a9 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -26,6 +26,8 @@ type Trojan struct { gunTLSConfig *tls.Config gunConfig *gun.Config transport *gun.TransportWrap + + realityConfig *tlsC.RealityConfig } type TrojanOption struct { @@ -84,7 +86,7 @@ func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) } if t.transport != nil { - c, err = gun.StreamGunWithConn(c, t.gunTLSConfig, t.gunConfig) + c, err = gun.StreamGunWithConn(c, t.gunTLSConfig, t.gunConfig, t.realityConfig) } else { c, err = t.plainStream(c) } @@ -245,12 +247,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { tOption.ServerName = option.SNI } - var err error - tOption.Reality, err = option.RealityOpts.Parse() - if err != nil { - return nil, err - } - t := &Trojan{ Base: &Base{ name: option.Name, @@ -266,6 +262,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { option: &option, } + var err error + t.realityConfig, err = option.RealityOpts.Parse() + if err != nil { + return nil, err + } + tOption.Reality = t.realityConfig + if option.Network == "grpc" { dialFn := func(network, addr string) (net.Conn, error) { c, err := dialer.DialContext(context.Background(), "tcp", t.addr, t.Base.DialOptions()...) @@ -292,7 +295,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { } } - t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, tOption.ClientFingerprint) + t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, tOption.ClientFingerprint, t.realityConfig) t.gunTLSConfig = tlsConfig t.gunConfig = &gun.Config{ diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 435beee9..5dc52571 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -156,7 +156,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { c, err = vmess.StreamH2Conn(c, h2Opts) case "grpc": - c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig) + c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig, v.realityConfig) default: // default tcp network // handle TLS And XTLS @@ -522,6 +522,11 @@ func NewVless(option VlessOption) (*Vless, error) { option: &option, } + v.realityConfig, err = v.option.RealityOpts.Parse() + if err != nil { + return nil, err + } + switch option.Network { case "h2": if len(option.HTTP2Opts.Host) == 0 { @@ -556,12 +561,7 @@ func NewVless(option VlessOption) (*Vless, error) { v.gunTLSConfig = tlsConfig v.gunConfig = gunConfig - v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint) - } - - v.realityConfig, err = v.option.RealityOpts.Parse() - if err != nil { - return nil, err + v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint, v.realityConfig) } return v, nil diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index f9e7205b..5bb46dad 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -193,7 +193,7 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { c, err = clashVMess.StreamH2Conn(c, h2Opts) case "grpc": - c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig) + c, err = gun.StreamGunWithConn(c, v.gunTLSConfig, v.gunConfig, v.realityConfig) default: // handle TLS if v.option.TLS { @@ -455,7 +455,7 @@ func NewVmess(option VmessOption) (*Vmess, error) { v.gunTLSConfig = tlsConfig v.gunConfig = gunConfig - v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint) + v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint, v.realityConfig) } v.realityConfig, err = v.option.RealityOpts.Parse() diff --git a/transport/gun/gun.go b/transport/gun/gun.go index 920e7adc..8eafdc50 100644 --- a/transport/gun/gun.go +++ b/transport/gun/gun.go @@ -20,6 +20,7 @@ import ( "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/common/pool" tlsC "github.com/Dreamacro/clash/component/tls" + "go.uber.org/atomic" "golang.org/x/net/http2" ) @@ -189,7 +190,7 @@ func (g *Conn) SetDeadline(t time.Time) error { return nil } -func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string) *TransportWrap { +func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, realityConfig *tlsC.RealityConfig) *TransportWrap { wrap := TransportWrap{} dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { @@ -201,20 +202,37 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string) *T wrap.remoteAddr = pconn.RemoteAddr() if len(Fingerprint) != 0 { - if fingerprint, exists := tlsC.GetFingerprint(Fingerprint); exists { - utlsConn := tlsC.UClient(pconn, cfg, fingerprint) - if err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx); err != nil { + if realityConfig == nil { + if fingerprint, exists := tlsC.GetFingerprint(Fingerprint); exists { + utlsConn := tlsC.UClient(pconn, cfg, fingerprint) + if err := utlsConn.(*tlsC.UConn).HandshakeContext(ctx); err != nil { + pconn.Close() + return nil, err + } + state := utlsConn.(*tlsC.UConn).ConnectionState() + if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + utlsConn.Close() + return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + } + return utlsConn, nil + } + } else { + realityConn, err := tlsC.GetRealityConn(ctx, pconn, Fingerprint, tlsConfig, realityConfig) + if err != nil { pconn.Close() return nil, err } - state := utlsConn.(*tlsC.UConn).ConnectionState() - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { - utlsConn.Close() - return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) - } - return utlsConn, nil + //state := realityConn.(*utls.UConn).ConnectionState() + //if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + // realityConn.Close() + // return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + //} + return realityConn, nil } } + if realityConfig != nil { + return nil, errors.New("REALITY is based on uTLS, please set a client-fingerprint") + } conn := tls.Client(pconn, cfg) if err := conn.HandshakeContext(ctx); err != nil { @@ -274,11 +292,11 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er return conn, nil } -func StreamGunWithConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) { +func StreamGunWithConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config, realityConfig *tlsC.RealityConfig) (net.Conn, error) { dialFn := func(network, addr string) (net.Conn, error) { return conn, nil } - transport := NewHTTP2Client(dialFn, tlsConfig, cfg.ClientFingerprint) + transport := NewHTTP2Client(dialFn, tlsConfig, cfg.ClientFingerprint, realityConfig) return StreamGunWithTransport(transport, cfg) } From 9ae0bd9c2bdbc8c6b497dc4c4023502aa2e14799 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 12:06:40 +0800 Subject: [PATCH 075/149] fix: don't return a non-nil interface containing nil pointer --- component/dialer/dialer.go | 4 ++-- go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3014b812..84cc9c8d 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -170,7 +170,7 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, select { case results <- result: case <-returned: - if result.Conn != nil { + if result.Conn != nil && result.error == nil { _ = result.Conn.Close() } } @@ -218,7 +218,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, select { case results <- result: case <-returned: - if result.Conn != nil { + if result.Conn != nil && result.error == nil { _ = result.Conn.Close() } } diff --git a/go.mod b/go.mod index 26e47b8c..e5f99cf1 100644 --- a/go.mod +++ b/go.mod @@ -21,12 +21,12 @@ require ( github.com/metacubex/quic-go v0.32.0 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3 - github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 + github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb 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.20230307054559-0560a4da412b + github.com/sagernet/sing v0.1.8 github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d diff --git a/go.sum b/go.sum index 49666074..cf1a9a50 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 h1:Nn 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.20230304153753-5058534177f3 h1:oQLThm1a8E7hHmoM9XF2cO0FZPsHVynC4YXW4b3liUI= github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3/go.mod h1:b/19bRRhwampNPV+1gVDyDsQHmuGDaplxPQkwJh1kj4= -github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4 h1:d96mCF/LYyC9kULd2xwcXfP0Jd8klrOngmRxuUIZg/8= -github.com/metacubex/sing-wireguard v0.0.0-20230213124601-d04406a109b4/go.mod h1:p2VpJuxRefgVMxc8cmatMGSFNvYbjMYMsXJOe7qFstw= +github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8= +github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= @@ -127,8 +127,8 @@ 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.20230307054559-0560a4da412b h1:wxqf3O+cLHm1ZWEQG1DRwApwLlTV/NLKGqF1kNCk3Ms= -github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.1.8 h1:6DKo2FkSHn0nUcjO7bAext/ai7y7pCusK/+fScBJ5Jk= +github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvVDRpvv2F+RtECJN+VbuKjLtmQculQvOecls= From 6fe7f4641ed69d117ccb352d41fecb0190dbd81a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 12:26:17 +0800 Subject: [PATCH 076/149] fix: tuic server set authentication timeout after quic handshake complete --- transport/tuic/server.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/transport/tuic/server.go b/transport/tuic/server.go index fb8a30d3..19398fde 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -76,7 +76,7 @@ func (s *Server) Close() error { type serverHandler struct { *Server - quicConn quic.Connection + quicConn quic.EarlyConnection uuid uuid.UUID authCh chan struct{} @@ -87,13 +87,6 @@ type serverHandler struct { } func (s *serverHandler) handle() { - time.AfterFunc(s.AuthenticationTimeout, func() { - s.authOnce.Do(func() { - _ = s.quicConn.CloseWithError(AuthenticationTimeout, "AuthenticationTimeout") - s.authOk = false - close(s.authCh) - }) - }) go func() { _ = s.handleUniStream() }() @@ -103,6 +96,15 @@ func (s *serverHandler) handle() { go func() { _ = s.handleMessage() }() + + <-s.quicConn.HandshakeComplete().Done() + time.AfterFunc(s.AuthenticationTimeout, func() { + s.authOnce.Do(func() { + _ = s.quicConn.CloseWithError(AuthenticationTimeout, "AuthenticationTimeout") + s.authOk = false + close(s.authCh) + }) + }) } func (s *serverHandler) handleMessage() (err error) { From fe298bd53fe391a1ddfcf2bf6080bbc00e7f996e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 12:47:01 +0800 Subject: [PATCH 077/149] fix: strategyRoundRobin not begin with zero --- adapter/outboundgroup/loadbalance.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 703444da..1ed80496 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -131,21 +131,23 @@ func strategyRoundRobin() strategyFn { idx := 0 idxMutex := sync.Mutex{} return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { - id := idx // value could be wrong due to no lock, but don't care if we don't touch + idxMutex.Lock() + defer idxMutex.Unlock() + + i := 0 + length := len(proxies) + if touch { - idxMutex.Lock() - defer idxMutex.Unlock() - id = idx // get again by lock's protect, so it must be right defer func() { - idx = id + idx = (idx + i) % length }() } - length := len(proxies) - for i := 0; i < length; i++ { - id = (id + 1) % length + for ; i < length; i++ { + id := (idx + i) % length proxy := proxies[id] if proxy.Alive() { + i++ return proxy } } From e7f907291193d05d1218cdb1307980c6890a8565 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 12:54:43 +0800 Subject: [PATCH 078/149] fix: add xtls-rprx-vision server version warning to user --- adapter/outbound/vless.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 5dc52571..757661cc 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -17,6 +17,7 @@ import ( "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/vless" @@ -482,7 +483,10 @@ func NewVless(option VlessOption) (*Vless, error) { if option.Network != "ws" && len(option.Flow) >= 16 { option.Flow = option.Flow[:16] switch option.Flow { - case vless.XRO, vless.XRD, vless.XRS, vless.XRV: + case vless.XRV: + log.Warnln("To use %s, ensure your server is upgrade to Xray-core v1.8.0+", vless.XRV) + fallthrough + case vless.XRO, vless.XRD, vless.XRS: addons = &vless.Addons{ Flow: option.Flow, } From d309c6311dce876711be90677cf9c7ec259354a7 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 10 Mar 2023 13:42:09 +0800 Subject: [PATCH 079/149] chore: add reality-grpc --- docs/config.yaml | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index 971ca124..b0d4541d 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -59,10 +59,9 @@ hosts: # '.dev': 127.0.0.1 # 'alpha.clash.dev': '::1' -profile: - # 存储 select 选择记录 +profile: # 存储 select 选择记录 store-selected: false - + # 持久化 fake-ip store-fake-ip: true @@ -273,7 +272,7 @@ proxies: # socks5 # obfs-opts: # mode: http # or tls # host: bing.com - + # Shadowsocks # cipher支持: # aes-128-gcm aes-192-gcm aes-256-gcm @@ -296,7 +295,7 @@ proxies: # socks5 # UDP 则为双栈解析,获取结果中的第一个 IPv4 # ipv6-prefer 同 ipv4-prefer # 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效 - + - name: "ss2" type: ss server: server @@ -435,12 +434,11 @@ proxies: # socks5 network: tcp tls: true udp: true - xudp: true - flow: xtls-rprx-vision + flow: xtls-rprx-vision client-fingerprint: chrome # fingerprint: xxxx # skip-cert-verify: true - + - name: "vless-reality-vision" type: vless server: server @@ -449,14 +447,31 @@ proxies: # socks5 network: tcp tls: true udp: true - xudp: true flow: xtls-rprx-vision servername: www.microsoft.com # REALITY servername reality-opts: public-key: xxx short-id: xxx # optional client-fingerprint: chrome # cannot be empty - + + - name: "vless-reality-grpc" + type: vless + server: server + port: 443 + uuid: uuid + network: grpc + tls: true + udp: true + flow: + # skip-cert-verify: true + client-fingerprint: chrome + servername: testingcf.jsdelivr.net + grpc-opts: + grpc-service-name: "grpc" + reality-opts: + public-key: CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE + short-id: 10f897e26c4b9478 + - name: "vless-ws" type: vless server: server @@ -528,7 +543,7 @@ proxies: # socks5 # sni: example.com # aka server name # skip-cert-verify: true # fingerprint: xxxx - + #hysteria - name: "hysteria" type: hysteria @@ -588,7 +603,7 @@ proxies: # socks5 # skip-cert-verify: true # max-open-streams: 20 # default 100, too many open streams may hurt performance # sni: example.com - + # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss # The supported obfses: @@ -717,7 +732,7 @@ rules: - IP-CIDR,1.1.1.1/32,ss1 - IP-CIDR6,2409::/64,DIRECT # 当满足条件是 TCP 或 UDP 流量时,使用名为 sub-rule-name1 的规则集 - - SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1 + - SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1 - SUB-RULE,(AND,((NETWORK,UDP))),sub-rule-name2 # 定义多个子规则集,规则将以分叉匹配,使用 SUB-RULE 使用 # google.com(not match)--> baidu.com(match) From 7cc1c1b5617b30eaac9d1bcfa5af784047e7c621 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 10 Mar 2023 14:12:18 +0800 Subject: [PATCH 080/149] chore: adjust error log --- component/dialer/dialer.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 84cc9c8d..5ba00e31 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -180,14 +180,17 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, go racer(ipv4s, preferIPVersion != 6) go racer(ipv6s, preferIPVersion != 4) var fallback dialResult - var err error + var errs []error for { select { case <-ctx.Done(): if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil } - return nil, fmt.Errorf("dual stack connect failed: %w", err) + if res, ok := <-results; ok && res.error == nil { + return res.Conn, nil + } + return nil, errorsJoin(errs...) case <-fallbackTicker.C: if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil @@ -199,7 +202,11 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, } fallback = res } else { - err = res.error + if res.isPrimary { + errs = append([]error{fmt.Errorf("connect failed: %w", res.error)}, errs...) + } else { + errs = append(errs, fmt.Errorf("connect failed: %w", res.error)) + } } } } @@ -230,12 +237,12 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, for _, ip := range ips { go racer(ctx, ip) } - var err error + var errs []error for { select { case <-ctx.Done(): - if err != nil { - return nil, err + if len(errs) > 0 { + return nil, errorsJoin(errs...) } if ctx.Err() == context.DeadlineExceeded { return nil, os.ErrDeadlineExceeded @@ -245,7 +252,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, if res.error == nil { return res.Conn, nil } - err = res.error + errs = append(errs, res.error) } } } From 2c4783ff8b197e778b32558867897fa00ad0bbd8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 16:17:43 +0800 Subject: [PATCH 081/149] fix: SA4001 for netDialer copy --- component/dialer/dialer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 5ba00e31..b47b9e5b 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -116,7 +116,8 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po case nil: netDialer = &net.Dialer{} case *net.Dialer: - netDialer = &*netDialer.(*net.Dialer) // make a copy + _netDialer := *netDialer.(*net.Dialer) + netDialer = &_netDialer // make a copy default: return netDialer.DialContext(ctx, network, address) } From 2ccef31f754d1dab6734a3a3e358b01333ec01e9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 10 Mar 2023 17:00:39 +0800 Subject: [PATCH 082/149] fix: ensure wireguard inner use dialer with DefaultResolver --- adapter/outbound/wireguard.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 51c9ecb9..7eae30fc 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -221,6 +221,7 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts return nil, w.startErr } if !metadata.Resolved() { + options = append(options, dialer.WithResolver(resolver.DefaultResolver)) options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice})) conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress()) } else { From 5bcfe1a6c620a791ec25e23c22b2eb8ccd182eb7 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 10 Mar 2023 20:16:14 +0800 Subject: [PATCH 083/149] fix: dialer dual stack panic --- component/dialer/dialer.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index b47b9e5b..479def67 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -182,16 +182,8 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, go racer(ipv6s, preferIPVersion != 4) var fallback dialResult var errs []error - for { + for i := 0; i < 2; i++ { select { - case <-ctx.Done(): - if fallback.error == nil && fallback.Conn != nil { - return fallback.Conn, nil - } - if res, ok := <-results; ok && res.error == nil { - return res.Conn, nil - } - return nil, errorsJoin(errs...) case <-fallbackTicker.C: if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil @@ -211,6 +203,10 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, } } } + if fallback.error == nil && fallback.Conn != nil { + return fallback.Conn, nil + } + return nil, errorsJoin(errs...) } func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { From 8c135e4a915bf48ce9265ca55072f04e04e9e4bd Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 10 Mar 2023 20:48:18 +0800 Subject: [PATCH 084/149] chore: adjust log --- config/config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index efb5b4cf..85b1fafa 100644 --- a/config/config.go +++ b/config/config.go @@ -1255,8 +1255,10 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { } } } else { - // Deprecated: Use Sniff instead - log.Warnln("Deprecated: Use Sniff instead") + if sniffer.Enable { + // Deprecated: Use Sniff instead + log.Warnln("Deprecated: Use Sniff instead") + } globalPorts, err := parsePortRange(snifferRaw.Ports) if err != nil { return nil, err From 913ed62095428f99f93697274f7efd30a18e2d4f Mon Sep 17 00:00:00 2001 From: H1JK Date: Fri, 10 Mar 2023 20:53:39 +0800 Subject: [PATCH 085/149] fix: ALPN not applied in uTLS/REALITY --- component/tls/reality.go | 1 + component/tls/utls.go | 1 + transport/gun/gun.go | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index 732613d8..dbd4bf41 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -41,6 +41,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string } uConfig := &utls.Config{ ServerName: tlsConfig.ServerName, + NextProtos: tlsConfig.NextProtos, InsecureSkipVerify: true, SessionTicketsDisabled: true, VerifyPeerCertificate: verifier.VerifyPeerCertificate, diff --git a/component/tls/utls.go b/component/tls/utls.go index a7189aa8..e08ca7ee 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -89,6 +89,7 @@ func copyConfig(c *tls.Config) *utls.Config { return &utls.Config{ RootCAs: c.RootCAs, ServerName: c.ServerName, + NextProtos: c.NextProtos, InsecureSkipVerify: c.InsecureSkipVerify, VerifyPeerCertificate: c.VerifyPeerCertificate, } diff --git a/transport/gun/gun.go b/transport/gun/gun.go index 8eafdc50..ae2ea6a4 100644 --- a/transport/gun/gun.go +++ b/transport/gun/gun.go @@ -217,7 +217,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re return utlsConn, nil } } else { - realityConn, err := tlsC.GetRealityConn(ctx, pconn, Fingerprint, tlsConfig, realityConfig) + realityConn, err := tlsC.GetRealityConn(ctx, pconn, Fingerprint, cfg, realityConfig) if err != nil { pconn.Close() return nil, err From 035d878a9ff3eab2fabcb2de65afd6139bf7af96 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 10 Mar 2023 22:08:01 +0800 Subject: [PATCH 086/149] fix: dial panic --- component/dialer/dialer.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 479def67..d70e9173 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -182,13 +182,14 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, go racer(ipv6s, preferIPVersion != 4) var fallback dialResult var errs []error - for i := 0; i < 2; i++ { + for i := 0; i < 2; { select { case <-fallbackTicker.C: if fallback.error == nil && fallback.Conn != nil { return fallback.Conn, nil } case res := <-results: + i++ if res.error == nil { if res.isPrimary { return res.Conn, nil @@ -217,7 +218,7 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, returned := make(chan struct{}) defer close(returned) racer := func(ctx context.Context, ip netip.Addr) { - result := dialResult{isPrimary: true} + result := dialResult{isPrimary: true, ip: ip} defer func() { select { case results <- result: @@ -227,7 +228,6 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, } } }() - result.ip = ip result.Conn, result.error = dialContext(ctx, network, ip, port, opt) } @@ -235,23 +235,18 @@ func parallelDialContext(ctx context.Context, network string, ips []netip.Addr, go racer(ctx, ip) } var errs []error - for { - select { - case <-ctx.Done(): - if len(errs) > 0 { - return nil, errorsJoin(errs...) - } - if ctx.Err() == context.DeadlineExceeded { - return nil, os.ErrDeadlineExceeded - } - return nil, ctx.Err() - case res := <-results: - if res.error == nil { - return res.Conn, nil - } - errs = append(errs, res.error) + for i := 0; i < len(ips); i++ { + res := <-results + if res.error == nil { + return res.Conn, nil } + errs = append(errs, res.error) } + + if len(errs) > 0 { + return nil, errorsJoin(errs...) + } + return nil, os.ErrDeadlineExceeded } func serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { From 07f3cd2ae5576043064757cf511a5c624e722a2d Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 10 Mar 2023 23:38:16 +0800 Subject: [PATCH 087/149] chore: exposure ipv6 wait time --- config/config.go | 4 ++++ dns/resolver.go | 37 ++++++++++++++++++++++++------------- docs/config.yaml | 2 +- hub/executor/executor.go | 1 + 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/config/config.go b/config/config.go index 85b1fafa..9316f920 100644 --- a/config/config.go +++ b/config/config.go @@ -92,6 +92,7 @@ type DNS struct { Enable bool `yaml:"enable"` PreferH3 bool `yaml:"prefer-h3"` IPv6 bool `yaml:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout"` NameServer []dns.NameServer `yaml:"nameserver"` Fallback []dns.NameServer `yaml:"fallback"` FallbackFilter FallbackFilter `yaml:"fallback-filter"` @@ -171,6 +172,7 @@ type RawDNS struct { Enable bool `yaml:"enable"` PreferH3 bool `yaml:"prefer-h3"` IPv6 bool `yaml:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout"` UseHosts bool `yaml:"use-hosts"` NameServer []string `yaml:"nameserver"` Fallback []string `yaml:"fallback"` @@ -377,6 +379,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Enable: false, IPv6: false, UseHosts: true, + IPv6Timeout: 100, EnhancedMode: C.DNSMapping, FakeIPRange: "198.18.0.1/16", FallbackFilter: RawFallbackFilter{ @@ -1048,6 +1051,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R Enable: cfg.Enable, Listen: cfg.Listen, PreferH3: cfg.PreferH3, + IPv6Timeout: cfg.IPv6Timeout, IPv6: cfg.IPv6, EnhancedMode: cfg.EnhancedMode, FallbackFilter: FallbackFilter{ diff --git a/dns/resolver.go b/dns/resolver.go index 59b1ee06..69725870 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -42,6 +42,7 @@ type geositePolicyRecord struct { type Resolver struct { ipv6 bool + ipv6Timeout time.Duration hosts *trie.DomainTrie[netip.Addr] main []dnsClient fallback []dnsClient @@ -91,14 +92,20 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, }() ips, err = r.lookupIP(ctx, host, D.TypeA) - + var waitIPv6 *time.Timer + if r != nil { + waitIPv6 = time.NewTimer(r.ipv6Timeout) + } else { + waitIPv6 = time.NewTimer(100 * time.Millisecond) + } + defer waitIPv6.Stop() select { case ipv6s, open := <-ch: if !open && err != nil { return nil, resolver.ErrIPNotFound } ips = append(ips, ipv6s...) - case <-time.After(30 * time.Millisecond): + case <-waitIPv6.C: // wait ipv6 result } @@ -419,6 +426,7 @@ type Config struct { Default []NameServer ProxyServer []NameServer IPv6 bool + IPv6Timeout uint EnhancedMode C.DNSMode FallbackFilter FallbackFilter Pool *fakeip.Pool @@ -428,15 +436,17 @@ type Config struct { func NewResolver(config Config) *Resolver { defaultResolver := &Resolver{ - main: transform(config.Default, nil), - lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), + main: transform(config.Default, nil), + lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } r := &Resolver{ - ipv6: config.IPv6, - main: transform(config.Main, defaultResolver), - lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), - hosts: config.Hosts, + ipv6: config.IPv6, + main: transform(config.Main, defaultResolver), + lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } if len(config.Fallback) != 0 { @@ -502,11 +512,12 @@ func NewResolver(config Config) *Resolver { func NewProxyServerHostResolver(old *Resolver) *Resolver { r := &Resolver{ - ipv6: old.ipv6, - main: old.proxyServer, - lruCache: old.lruCache, - hosts: old.hosts, - policy: old.policy, + ipv6: old.ipv6, + main: old.proxyServer, + lruCache: old.lruCache, + hosts: old.hosts, + policy: old.policy, + ipv6Timeout: old.ipv6Timeout, } return r } diff --git a/docs/config.yaml b/docs/config.yaml index b0d4541d..b5af798b 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -166,7 +166,7 @@ dns: prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 - + # ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms # 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名 # 只能使用纯 IP 地址,可使用加密 DNS default-nameserver: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index d6ff7851..1bd22385 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -190,6 +190,7 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Main: c.NameServer, Fallback: c.Fallback, IPv6: c.IPv6 && generalIPv6, + IPv6Timeout: c.IPv6Timeout, EnhancedMode: c.EnhancedMode, Pool: c.FakeIPRange, Hosts: c.Hosts, From ae4d114802c12d3685e819e6ec89471bb3234caa Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 11 Mar 2023 12:23:27 +0800 Subject: [PATCH 088/149] chore: Cleanup REALITY code --- adapter/outbound/reality.go | 4 ++-- common/utils/must.go | 8 ++++++++ component/tls/reality.go | 15 ++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 common/utils/must.go diff --git a/adapter/outbound/reality.go b/adapter/outbound/reality.go index 9d892f96..23314e5f 100644 --- a/adapter/outbound/reality.go +++ b/adapter/outbound/reality.go @@ -24,8 +24,8 @@ func (o RealityOptions) Parse() (*tlsC.RealityConfig, error) { return nil, errors.New("invalid REALITY public key") } - config.ShortID, err = hex.DecodeString(o.ShortID) - if err != nil { + n, err = hex.Decode(config.ShortID[:], []byte(o.ShortID)) + if err != nil || n > tlsC.RealityMaxShortIDLen { return nil, errors.New("invalid REALITY short ID") } diff --git a/common/utils/must.go b/common/utils/must.go new file mode 100644 index 00000000..2dd5ff5e --- /dev/null +++ b/common/utils/must.go @@ -0,0 +1,8 @@ +package utils + +func MustOK[T any](result T, ok bool) T { + if ok { + return result + } + panic("operation failed") +} diff --git a/component/tls/reality.go b/component/tls/reality.go index dbd4bf41..a060de92 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -20,6 +20,7 @@ import ( "time" "unsafe" + "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/log" utls "github.com/sagernet/utls" @@ -29,9 +30,11 @@ import ( "golang.org/x/net/http2" ) +const RealityMaxShortIDLen = 8 + type RealityConfig struct { PublicKey [curve25519.ScalarSize]byte - ShortID []byte + ShortID [RealityMaxShortIDLen]byte } func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) { @@ -73,7 +76,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string hello.SessionId[0] = 1 hello.SessionId[1] = 7 hello.SessionId[2] = 5 - copy(hello.SessionId[8:], realityConfig.ShortID) + copy(hello.SessionId[8:], realityConfig.ShortID[:]) //log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16]) @@ -112,7 +115,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) { defer uConn.Close() - client := &http.Client{ + client := http.Client{ Transport: &http2.Transport{ DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) { return uConn, nil @@ -139,9 +142,11 @@ type realityVerifier struct { verified bool } +var pOffset = utils.MustOK(reflect.TypeOf((*utls.UConn)(nil)).Elem().FieldByName("peerCertificates")).Offset + func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") - certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset)) + //p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates") + certs := *(*[]*x509.Certificate)(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + pOffset)) if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok { h := hmac.New(sha512.New, c.authKey) h.Write(pub) From 794452218808dc8ca7648166da9cb8a3603fd6ef Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 Mar 2023 09:38:58 +0800 Subject: [PATCH 089/149] chore: update quic-go --- go.mod | 7 +++---- go.sum | 14 ++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index e5f99cf1..bde63cc3 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/jpillora/backoff v1.0.0 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/quic-go v0.33.1 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb @@ -69,9 +69,8 @@ require ( github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-18 v0.2.0 // indirect - github.com/quic-go/qtls-go1-19 v0.2.0 // indirect - github.com/quic-go/qtls-go1-20 v0.1.0 // indirect + github.com/quic-go/qtls-go1-19 v0.2.1 // indirect + github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect diff --git a/go.sum b/go.sum index cf1a9a50..7b24bc35 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005 h1:0TEvReK/D6YLszjGj/bdx4d7amQSjQ2X/98r4ZiUbxU= github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= -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/quic-go v0.33.1 h1:ZIxZFGivpSLOEZuuNkLy+aPvo1RP4uRBjNg3SAkXwIg= +github.com/metacubex/quic-go v0.33.1/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= 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.20230304153753-5058534177f3 h1:oQLThm1a8E7hHmoM9XF2cO0FZPsHVynC4YXW4b3liUI= @@ -115,12 +115,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U= -github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc= -github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk= -github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI= -github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= +github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= +github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= From 09b4a7ff1584ccc57c9e2e342dfa19dc9cd682c2 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sun, 12 Mar 2023 10:13:23 +0800 Subject: [PATCH 090/149] chore: Remove useless mutex in Vision --- transport/vless/vision.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/transport/vless/vision.go b/transport/vless/vision.go index 857d9150..8dc84e40 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -3,7 +3,6 @@ package vless import ( "bytes" "encoding/binary" - "sync" "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/log" @@ -20,12 +19,9 @@ const ( commandPaddingDirect byte = 0x02 ) -var mutex sync.RWMutex - func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(len(p)) var paddingLen int32 - mutex.Lock() if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") @@ -34,8 +30,7 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid paddingLen = fastrand.Int31n(256) } } - mutex.Unlock() - if userUUID != nil { // unnecessary, but keep the same with Xray + if userUUID != nil { buffer.Write(userUUID.Bytes()) } @@ -51,7 +46,6 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, paddingTLS bool) { contentLen := int32(buffer.Len()) var paddingLen int32 - mutex.Lock() if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") @@ -60,12 +54,11 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding paddingLen = fastrand.Int31n(256) } } - mutex.Unlock() binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen)) binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen)) buffer.ExtendHeader(1)[0] = command - if userUUID != nil { // unnecessary, but keep the same with Xray + if userUUID != nil { copy(buffer.ExtendHeader(uuid.Size), userUUID.Bytes()) } From 4b72ae7aaba685afdf9538d04971d86b7a175172 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:35:59 +0800 Subject: [PATCH 091/149] fix: global-client-fingerprint is now work --- config/config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index 9316f920..817d0d64 100644 --- a/config/config.go +++ b/config/config.go @@ -446,6 +446,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.General = general + if len(config.General.GlobalClientFingerprint) != 0 { + log.Debugln("GlobalClientFingerprint:%s", config.General.GlobalClientFingerprint) + tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) + } + dialer.DefaultInterface.Store(config.General.Interface) proxies, providers, err := parseProxies(rawCfg) if err != nil { @@ -521,11 +526,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm - if len(config.General.GlobalClientFingerprint) != 0 { - log.Debugln("GlobalClientFingerprint:%s", config.General.GlobalClientFingerprint) - tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) - } - return config, nil } From 7f588935eac0bcd2c7165d7d484c0a02a0c99df6 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 12 Mar 2023 15:00:59 +0800 Subject: [PATCH 092/149] feta: add hosts support domain and mulitple ip (#439) * feat: host support domain and multiple ips * chore: append local address via `clash` * chore: update hosts demo * chore: unified parse mixed string and array * fix: flatten cname * chore: adjust logic * chore: reuse code * chore: use cname in tunnel * chore: try use domain mapping when normal dns * chore: format code --- common/utils/slice.go | 34 ++++++++++ component/resolver/host.go | 112 +++++++++++++++++++++++++++++++++ component/resolver/resolver.go | 23 ++++--- config/config.go | 74 +++++++++++++--------- dns/middleware.go | 79 +++++++++++++++-------- dns/resolver.go | 4 +- dns/util.go | 2 +- docs/config.yaml | 3 + hub/executor/executor.go | 4 +- hub/hub.go | 2 +- tunnel/tunnel.go | 17 +++-- 11 files changed, 278 insertions(+), 76 deletions(-) create mode 100644 common/utils/slice.go create mode 100644 component/resolver/host.go diff --git a/common/utils/slice.go b/common/utils/slice.go new file mode 100644 index 00000000..1b0fa494 --- /dev/null +++ b/common/utils/slice.go @@ -0,0 +1,34 @@ +package utils + +import ( + "errors" + "fmt" + "reflect" +) + +func Filter[T comparable](tSlice []T, filter func(t T) bool) []T { + result := make([]T, 0) + for _, t := range tSlice { + if filter(t) { + result = append(result, t) + } + } + return result +} + +func ToStringSlice(value any) ([]string, error) { + strArr := make([]string, 0) + switch reflect.TypeOf(value).Kind() { + case reflect.Slice, reflect.Array: + origin := reflect.ValueOf(value) + for i := 0; i < origin.Len(); i++ { + item := fmt.Sprintf("%v", origin.Index(i)) + strArr = append(strArr, item) + } + case reflect.String: + strArr = append(strArr, fmt.Sprintf("%v", value)) + default: + return nil, errors.New("value format error, must be string or array") + } + return strArr, nil +} diff --git a/component/resolver/host.go b/component/resolver/host.go new file mode 100644 index 00000000..ca90cd27 --- /dev/null +++ b/component/resolver/host.go @@ -0,0 +1,112 @@ +package resolver + +import ( + "errors" + "math/rand" + "net/netip" + "strings" + + "github.com/Dreamacro/clash/common/utils" + "github.com/Dreamacro/clash/component/trie" +) + +type Hosts struct { + *trie.DomainTrie[HostValue] +} + +func NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts { + return Hosts{ + hosts, + } +} + +func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { + value := h.DomainTrie.Search(domain) + if value == nil { + return nil, false + } + hostValue := value.Data() + for { + if isDomain && hostValue.IsDomain { + return &hostValue, true + } else { + if node := h.DomainTrie.Search(hostValue.Domain); node != nil { + hostValue = node.Data() + } else { + break + } + } + } + if isDomain == hostValue.IsDomain { + return &hostValue, true + } + return &hostValue, false +} + +type HostValue struct { + IsDomain bool + IPs []netip.Addr + Domain string +} + +func NewHostValue(value any) (HostValue, error) { + isDomain := true + ips := make([]netip.Addr, 0) + domain := "" + if valueArr, err := utils.ToStringSlice(value); err != nil { + return HostValue{}, err + } else { + if len(valueArr) > 1 { + isDomain = false + for _, str := range valueArr { + if ip, err := netip.ParseAddr(str); err == nil { + ips = append(ips, ip) + } else { + return HostValue{}, err + } + } + } else if len(valueArr) == 1 { + host := valueArr[0] + if ip, err := netip.ParseAddr(host); err == nil { + ips = append(ips, ip) + isDomain = false + } else { + domain = host + } + } + } + if isDomain { + return NewHostValueByDomain(domain) + } else { + return NewHostValueByIPs(ips) + } +} + +func NewHostValueByIPs(ips []netip.Addr) (HostValue, error) { + if len(ips) == 0 { + return HostValue{}, errors.New("ip list is empty") + } + return HostValue{ + IsDomain: false, + IPs: ips, + }, nil +} + +func NewHostValueByDomain(domain string) (HostValue, error) { + domain = strings.Trim(domain, ".") + item := strings.Split(domain, ".") + if len(item) < 2 { + return HostValue{}, errors.New("invaild domain") + } + return HostValue{ + IsDomain: true, + Domain: domain, + }, nil +} + +func (hv HostValue) RandIP() (netip.Addr, error) { + if hv.IsDomain { + return netip.Addr{}, errors.New("value type is error") + } + return hv.IPs[rand.Intn(len(hv.IPs)-1)], nil +} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 6ae2d7c2..f5872ad7 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/trie" "github.com/miekg/dns" @@ -27,7 +28,7 @@ var ( DisableIPv6 = true // DefaultHosts aim to resolve hosts - DefaultHosts = trie.New[netip.Addr]() + DefaultHosts = NewHosts(trie.New[HostValue]()) // DefaultDNSTimeout defined the default dns request timeout DefaultDNSTimeout = time.Second * 5 @@ -51,9 +52,11 @@ type Resolver interface { // LookupIPv4WithResolver same as LookupIPv4, but with a resolver func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { - if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data(); ip.Is4() { - return []netip.Addr{node.Data()}, nil + if node, ok := DefaultHosts.Search(host, false); ok { + if addrs := utils.Filter(node.IPs, func(ip netip.Addr) bool { + return ip.Is4() + }); len(addrs) > 0 { + return addrs, nil } } @@ -106,9 +109,11 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net return nil, ErrIPv6Disabled } - if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data(); ip.Is6() { - return []netip.Addr{ip}, nil + if node, ok := DefaultHosts.Search(host, false); ok { + if addrs := utils.Filter(node.IPs, func(ip netip.Addr) bool { + return ip.Is6() + }); len(addrs) > 0 { + return addrs, nil } } @@ -155,8 +160,8 @@ func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { // LookupIPWithResolver same as LookupIP, but with a resolver func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { - if node := DefaultHosts.Search(host); node != nil { - return []netip.Addr{node.Data()}, nil + if node, ok := DefaultHosts.Search(host, false); ok { + return node.IPs, nil } if r != nil { diff --git a/config/config.go b/config/config.go index 817d0d64..45dd31f5 100644 --- a/config/config.go +++ b/config/config.go @@ -9,7 +9,6 @@ import ( "net/netip" "net/url" "os" - "reflect" "runtime" "strconv" "strings" @@ -26,6 +25,7 @@ import ( "github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata/router" P "github.com/Dreamacro/clash/component/process" + "github.com/Dreamacro/clash/component/resolver" SNIFF "github.com/Dreamacro/clash/component/sniffer" tlsC "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/component/trie" @@ -100,7 +100,7 @@ type DNS struct { EnhancedMode C.DNSMode `yaml:"enhanced-mode"` DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` FakeIPRange *fakeip.Pool - Hosts *trie.DomainTrie[netip.Addr] + Hosts *trie.DomainTrie[resolver.HostValue] NameServerPolicy map[string][]dns.NameServer ProxyServerNameserver []dns.NameServer } @@ -154,7 +154,7 @@ type Config struct { IPTables *IPTables DNS *DNS Experimental *Experimental - Hosts *trie.DomainTrie[netip.Addr] + Hosts *trie.DomainTrie[resolver.HostValue] Profile *Profile Rules []C.Rule SubRules map[string][]C.Rule @@ -265,7 +265,7 @@ type RawConfig struct { Sniffer RawSniffer `yaml:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"` - Hosts map[string]string `yaml:"hosts"` + Hosts map[string]any `yaml:"hosts"` DNS RawDNS `yaml:"dns"` Tun RawTun `yaml:"tun"` TuicServer RawTuicServer `yaml:"tuic-server"` @@ -339,7 +339,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { UnifiedDelay: false, Authentication: []string{}, LogLevel: log.INFO, - Hosts: map[string]string{}, + Hosts: map[string]any{}, Rule: []string{}, Proxy: []map[string]any{}, ProxyGroup: []map[string]any{}, @@ -827,21 +827,47 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s return rules, nil } -func parseHosts(cfg *RawConfig) (*trie.DomainTrie[netip.Addr], error) { - tree := trie.New[netip.Addr]() +func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) { + tree := trie.New[resolver.HostValue]() // add default hosts - if err := tree.Insert("localhost", netip.AddrFrom4([4]byte{127, 0, 0, 1})); err != nil { + hostValue, _ := resolver.NewHostValueByIPs( + []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1})}) + if err := tree.Insert("localhost", hostValue); err != nil { log.Errorln("insert localhost to host error: %s", err.Error()) } if len(cfg.Hosts) != 0 { - for domain, ipStr := range cfg.Hosts { - ip, err := netip.ParseAddr(ipStr) - if err != nil { - return nil, fmt.Errorf("%s is not a valid IP", ipStr) + for domain, anyValue := range cfg.Hosts { + if str, ok := anyValue.(string); ok && str == "clash" { + if addrs, err := net.InterfaceAddrs(); err != nil { + log.Errorln("insert clash to host error: %s", err) + } else { + ips := make([]netip.Addr, 0) + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok { + if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil { + ips = append(ips, ip) + } + } + } + anyValue = ips + } } - _ = tree.Insert(domain, ip) + value, err := resolver.NewHostValue(anyValue) + if err != nil { + return nil, fmt.Errorf("%s is not a valid value", anyValue) + } + if value.IsDomain { + node := tree.Search(value.Domain) + for node != nil && node.Data().IsDomain { + if node.Data().Domain == domain { + return nil, fmt.Errorf("%s, there is a cycle in domain name mapping", domain) + } + node = tree.Search(node.Data().Domain) + } + } + _ = tree.Insert(domain, value) } } tree.Optimize() @@ -961,24 +987,12 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][ policy := map[string][]dns.NameServer{} for domain, server := range nsPolicy { - var ( - nameservers []dns.NameServer - err error - ) - switch reflect.TypeOf(server).Kind() { - case reflect.Slice, reflect.Array: - origin := reflect.ValueOf(server) - servers := make([]string, 0) - for i := 0; i < origin.Len(); i++ { - servers = append(servers, fmt.Sprintf("%v", origin.Index(i))) - } - nameservers, err = parseNameServer(servers, preferH3) - case reflect.String: - nameservers, err = parseNameServer([]string{fmt.Sprintf("%v", server)}, preferH3) - default: - return nil, errors.New("server format error, must be string or array") + servers, err := utils.ToStringSlice(server) + if err != nil { + return nil, err } + nameservers, err := parseNameServer(servers, preferH3) if err != nil { return nil, err } @@ -1041,7 +1055,7 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM return sites, nil } -func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.Rule) (*DNS, error) { +func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule) (*DNS, error) { cfg := rawCfg.DNS if cfg.Enable && len(cfg.NameServer) == 0 { return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") diff --git a/dns/middleware.go b/dns/middleware.go index 7dc9622d..f2dd9c96 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -8,7 +8,7 @@ import ( "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/nnip" "github.com/Dreamacro/clash/component/fakeip" - "github.com/Dreamacro/clash/component/trie" + R "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/context" "github.com/Dreamacro/clash/log" @@ -21,7 +21,7 @@ type ( middleware func(next handler) handler ) -func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip.Addr, string]) middleware { +func withHosts(hosts R.Hosts, mapping *cache.LruCache[netip.Addr, string]) middleware { return func(next handler) handler { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { q := r.Question[0] @@ -31,40 +31,68 @@ func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip } host := strings.TrimRight(q.Name, ".") - - record := hosts.Search(host) - if record == nil { + handleCName := func(resp *D.Msg, domain string) { + rr := &D.CNAME{} + rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeCNAME, Class: D.ClassINET, Ttl: 10} + rr.Target = domain + "." + resp.Answer = append([]D.RR{rr}, resp.Answer...) + } + record, ok := hosts.Search(host, q.Qtype != D.TypeA && q.Qtype != D.TypeAAAA) + if !ok { + if record != nil && record.IsDomain { + // replace request domain + newR := r.Copy() + newR.Question[0].Name = record.Domain + "." + resp, err := next(ctx, newR) + if err == nil { + resp.Id = r.Id + resp.Question = r.Question + handleCName(resp, record.Domain) + } + return resp, err + } return next(ctx, r) } - ip := record.Data() msg := r.Copy() - - if ip.Is4() && q.Qtype == D.TypeA { - rr := &D.A{} - rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: 10} - rr.A = ip.AsSlice() - - msg.Answer = []D.RR{rr} - } else if q.Qtype == D.TypeAAAA { - rr := &D.AAAA{} - rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10} - ip := ip.As16() - rr.AAAA = ip[:] - msg.Answer = []D.RR{rr} - } else { - return next(ctx, r) + handleIPs := func() { + for _, ipAddr := range record.IPs { + if ipAddr.Is4() && q.Qtype == D.TypeA { + rr := &D.A{} + rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: 10} + rr.A = ipAddr.AsSlice() + msg.Answer = append(msg.Answer, rr) + if mapping != nil { + mapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10)) + } + } else if q.Qtype == D.TypeAAAA { + rr := &D.AAAA{} + rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10} + ip := ipAddr.As16() + rr.AAAA = ip[:] + msg.Answer = append(msg.Answer, rr) + if mapping != nil { + mapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10)) + } + } + } } - if mapping != nil { - mapping.SetWithExpire(ip, host, time.Now().Add(time.Second*10)) + switch q.Qtype { + case D.TypeA: + handleIPs() + case D.TypeAAAA: + handleIPs() + case D.TypeCNAME: + handleCName(r, record.Domain) + default: + return next(ctx, r) } ctx.SetType(context.DNSTypeHost) msg.SetRcode(r, D.RcodeSuccess) msg.Authoritative = true msg.RecursionAvailable = true - return msg, nil } } @@ -149,6 +177,7 @@ func withFakeIP(fakePool *fakeip.Pool) middleware { func withResolver(resolver *Resolver) handler { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { ctx.SetType(context.DNSTypeRaw) + q := r.Question[0] // return a empty AAAA msg when ipv6 disabled @@ -183,7 +212,7 @@ func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler { middlewares := []middleware{} if resolver.hosts != nil { - middlewares = append(middlewares, withHosts(resolver.hosts, mapper.mapping)) + middlewares = append(middlewares, withHosts(R.NewHosts(resolver.hosts), mapper.mapping)) } if mapper.mode == C.DNSFakeIP { diff --git a/dns/resolver.go b/dns/resolver.go index 69725870..57f581a5 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -43,7 +43,7 @@ type geositePolicyRecord struct { type Resolver struct { ipv6 bool ipv6Timeout time.Duration - hosts *trie.DomainTrie[netip.Addr] + hosts *trie.DomainTrie[resolver.HostValue] main []dnsClient fallback []dnsClient fallbackDomainFilters []fallbackDomainFilter @@ -430,7 +430,7 @@ type Config struct { EnhancedMode C.DNSMode FallbackFilter FallbackFilter Pool *fakeip.Pool - Hosts *trie.DomainTrie[netip.Addr] + Hosts *trie.DomainTrie[resolver.HostValue] Policy map[string][]NameServer } diff --git a/dns/util.go b/dns/util.go index 203ab615..4821195d 100644 --- a/dns/util.go +++ b/dns/util.go @@ -66,7 +66,7 @@ func setMsgTTL(msg *D.Msg, ttl uint32) { } func isIPRequest(q D.Question) bool { - return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA) + return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA || q.Qtype == D.TypeCNAME) } func transform(servers []NameServer, resolver *Resolver) []dnsClient { diff --git a/docs/config.yaml b/docs/config.yaml index b5af798b..b52cc63f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -58,6 +58,9 @@ hosts: # '*.clash.dev': 127.0.0.1 # '.dev': 127.0.0.1 # 'alpha.clash.dev': '::1' +# test.com: [1.1.1.1, 2.2.2.2] +# clash.lan: clash # clash 为特别字段,将加入本地所有网卡的地址 +# baidu.com: google.com # 只允许配置一个别名 profile: # 存储 select 选择记录 store-selected: false diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 1bd22385..21a25ecd 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -226,8 +226,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { dns.ReCreateServer(c.Listen, r, m) } -func updateHosts(tree *trie.DomainTrie[netip.Addr]) { - resolver.DefaultHosts = tree +func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) { + resolver.DefaultHosts = resolver.NewHosts(tree) } func updateProxies(proxies map[string]C.Proxy, providers map[string]provider.ProxyProvider) { diff --git a/hub/hub.go b/hub/hub.go index e4c414fa..bd228fad 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -44,7 +44,7 @@ func Parse(options ...Option) error { if cfg.General.ExternalController != "" { go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, - cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey,cfg.General.LogLevel==log.DEBUG) + cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG) } executor.ApplyConfig(cfg, true) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index d80893c9..b686eae6 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -201,13 +201,18 @@ func preHandleMetadata(metadata *C.Metadata) error { if resolver.FakeIPEnabled() { metadata.DstIP = netip.Addr{} metadata.DNSMode = C.DNSFakeIP - } else if node := resolver.DefaultHosts.Search(host); node != nil { + } else if node, ok := resolver.DefaultHosts.Search(host, false); ok { // redir-host should lookup the hosts - metadata.DstIP = node.Data() + metadata.DstIP, _ = node.RandIP() + } else if node != nil && node.IsDomain { + metadata.Host = node.Domain } } else if resolver.IsFakeIP(metadata.DstIP) { return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) } + } else if node, ok := resolver.DefaultHosts.Search(metadata.Host, true); ok { + // try use domain mapping + metadata.Host = node.Domain } return nil @@ -392,8 +397,8 @@ func handleTCPConn(connCtx C.ConnContext) { dialMetadata := metadata if len(metadata.Host) > 0 { - if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { - if dstIp := node.Data(); !FakeIPRange().Contains(dstIp) { + if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok { + if dstIp, _ := node.RandIP(); !FakeIPRange().Contains(dstIp) { dialMetadata.DstIP = dstIp dialMetadata.DNSMode = C.DNSHosts dialMetadata = dialMetadata.Pure() @@ -498,8 +503,8 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { processFound bool ) - if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { - metadata.DstIP = node.Data() + if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok { + metadata.DstIP, _ = node.RandIP() resolved = true } From 074fee2b485e63cdeddde4e90a821db5c7ee6b08 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 12 Mar 2023 15:05:28 +0800 Subject: [PATCH 093/149] chore: add comment --- component/resolver/host.go | 1 + 1 file changed, 1 insertion(+) diff --git a/component/resolver/host.go b/component/resolver/host.go index ca90cd27..49168e2b 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -20,6 +20,7 @@ func NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts { } } +// Return the search result and whether to match the parameter `isDomain` func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { value := h.DomainTrie.Search(domain) if value == nil { From 0a6c848c9e89cc62a60a0dd210d621b06fa87aa6 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:42:41 +0800 Subject: [PATCH 094/149] feat: nameserver-policy support multiple keys e.g., nameserver-policy: # 'www.baidu.com': '114.114.114.114' # '+.internal.crop.com': '10.0.0.1' "geosite:cn,private,apple": - https://doh.pub/dns-query - https://dns.alidns.com/dns-query "www.baidu.com,+.google.cn": - 223.5.5.5 - 1.1.1.1 --- config/config.go | 29 +++++++++++++++++++++++++++-- docs/config.yaml | 7 ++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index 45dd31f5..c8c00664 100644 --- a/config/config.go +++ b/config/config.go @@ -4,11 +4,11 @@ import ( "container/list" "errors" "fmt" - "net" "net/netip" "net/url" "os" + "regexp" "runtime" "strconv" "strings" @@ -985,8 +985,33 @@ func parsePureDNSServer(server string) string { } func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][]dns.NameServer, error) { policy := map[string][]dns.NameServer{} + updatedPolicy := make(map[string]interface{}) + re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) - for domain, server := range nsPolicy { + for k, v := range nsPolicy { + if strings.Contains(k, "geosite:") { + subkeys := strings.Split(k, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + //log.Infoln("subkeys:%+v", subkeys) + for _, subkey := range subkeys { + newKey := "geosite:" + subkey + //log.Infoln("newKey:%+v", newKey) + updatedPolicy[newKey] = v + } + } else if re.MatchString(k) { + subkeys := strings.Split(k, ",") + //log.Infoln("subkeys:%+v", subkeys) + for _, subkey := range subkeys { + updatedPolicy[subkey] = v + } + } else { + updatedPolicy[k] = v + } + } + //log.Infoln("updatedPolicy:%+v", updatedPolicy) + + for domain, server := range updatedPolicy { servers, err := utils.ToStringSlice(server) if err != nil { diff --git a/docs/config.yaml b/docs/config.yaml index b52cc63f..e08a6e33 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -231,12 +231,13 @@ dns: # - '+.youtube.com' # 配置查询域名使用的 DNS 服务器 - nameserver-policy: # 'www.baidu.com': '114.114.114.114' + nameserver-policy: + # 'www.baidu.com': '114.114.114.114' # '+.internal.crop.com': '10.0.0.1' - "geosite:cn": + "geosite:cn,private,apple": - https://doh.pub/dns-query - https://dns.alidns.com/dns-query - "www.baidu.com": [https://doh.pub/dns-query, https://dns.alidns.com/dns-query] + "www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query] proxies: # socks5 - name: "socks" From 7d230139a06dbf02d4846f6e27e16ddd1598b893 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 12 Mar 2023 18:44:30 +0800 Subject: [PATCH 095/149] fix: rand ip error and `clash` remove loopback ip --- component/resolver/host.go | 4 ++-- config/config.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/component/resolver/host.go b/component/resolver/host.go index 49168e2b..3b7e9a37 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -2,12 +2,12 @@ package resolver import ( "errors" - "math/rand" "net/netip" "strings" "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/trie" + "github.com/zhangyunhao116/fastrand" ) type Hosts struct { @@ -109,5 +109,5 @@ func (hv HostValue) RandIP() (netip.Addr, error) { if hv.IsDomain { return netip.Addr{}, errors.New("value type is error") } - return hv.IPs[rand.Intn(len(hv.IPs)-1)], nil + return hv.IPs[fastrand.Intn(len(hv.IPs))], nil } diff --git a/config/config.go b/config/config.go index c8c00664..dfcb772e 100644 --- a/config/config.go +++ b/config/config.go @@ -845,7 +845,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) { } else { ips := make([]netip.Addr, 0) for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil { ips = append(ips, ip) } From 5de043acc6006fb00b3b434aff58d76d2eb685fa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 Mar 2023 19:03:03 +0800 Subject: [PATCH 096/149] fix: tuic relay tuic --- transport/tuic/conn.go | 30 ++++++++++++++++++++++++++---- transport/tuic/protocol.go | 20 +++++--------------- transport/tuic/server.go | 24 +----------------------- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index 7ecc3f0d..ee687425 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -200,7 +200,7 @@ func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err } func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if len(p) > q.maxUdpRelayPacketSize { + if q.udpRelayMode != "quic" && len(p) > q.maxUdpRelayPacketSize { return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.maxUdpRelayPacketSize) } if q.closed { @@ -215,7 +215,6 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro q.deferQuicConnFn(q.quicConn, err) }() } - addr.String() buf := pool.GetBuffer() defer pool.PutBuffer(buf) addrPort, err := netip.ParseAddrPort(addr.String()) @@ -239,7 +238,8 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro return } default: // native - err = q.quicConn.SendMessage(buf.Bytes()) + data := buf.Bytes() + err = q.quicConn.SendMessage(data) if err != nil { return } @@ -250,7 +250,29 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro } func (q *quicStreamPacketConn) LocalAddr() net.Addr { - return q.quicConn.LocalAddr() + addr := q.quicConn.LocalAddr() + if q.inputConn != nil { // client + return &packetAddr{addrStr: q.quicConn.LocalAddr().String(), connId: q.connId, rawAddr: addr} + } + return addr // server } var _ net.PacketConn = &quicStreamPacketConn{} + +type packetAddr struct { + addrStr string + connId uint32 + rawAddr net.Addr +} + +func (a packetAddr) Network() string { + return "tuic" +} + +func (a packetAddr) String() string { + return fmt.Sprintf("%s-%d", a.addrStr, a.connId) +} + +func (a packetAddr) RawAddr() net.Addr { + return a.rawAddr +} diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index a54c8e54..b14074a7 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -114,9 +114,6 @@ func NewAuthenticate(TKN [32]byte) Authenticate { func ReadAuthenticateWithHead(head CommandHead, reader BufferedReader) (c Authenticate, err error) { c.CommandHead = head - if err != nil { - return - } if c.CommandHead.TYPE != AuthenticateType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) return @@ -170,9 +167,6 @@ func NewConnect(ADDR Address) Connect { func ReadConnectWithHead(head CommandHead, reader BufferedReader) (c Connect, err error) { c.CommandHead = head - if err != nil { - return - } if c.CommandHead.TYPE != ConnectType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) return @@ -228,9 +222,6 @@ func NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet { func ReadPacketWithHead(head CommandHead, reader BufferedReader) (c Packet, err error) { c.CommandHead = head - if err != nil { - return - } if c.CommandHead.TYPE != PacketType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) return @@ -305,9 +296,6 @@ func NewDissociate(ASSOC_ID uint32) Dissociate { func ReadDissociateWithHead(head CommandHead, reader BufferedReader) (c Dissociate, err error) { c.CommandHead = head - if err != nil { - return - } if c.CommandHead.TYPE != DissociateType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) return @@ -476,15 +464,17 @@ func NewAddress(metadata *C.Metadata) Address { func NewAddressAddrPort(addrPort netip.AddrPort) Address { var addrType byte - if addrPort.Addr().Is4() { + port := addrPort.Port() + addr := addrPort.Addr().Unmap() + if addr.Is4() { addrType = AtypIPv4 } else { addrType = AtypIPv6 } return Address{ TYPE: addrType, - ADDR: addrPort.Addr().AsSlice(), - PORT: addrPort.Port(), + ADDR: addr.AsSlice(), + PORT: port, } } diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 19398fde..770e1869 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "crypto/tls" - "fmt" "net" "sync" "sync/atomic" @@ -154,14 +153,10 @@ func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err err return s.HandleUdpFn(packet.ADDR.SocksAddr(), &serverUDPPacket{ pc: pc, packet: &packet, - rAddr: s.genServerAssocIdAddr(assocId, s.quicConn.RemoteAddr()), + rAddr: &packetAddr{addrStr: "tuic-" + s.uuid.String(), connId: assocId, rawAddr: s.quicConn.RemoteAddr()}, }) } -func (s *serverHandler) genServerAssocIdAddr(assocId uint32, addr net.Addr) net.Addr { - return &ServerAssocIdAddr{assocId: fmt.Sprintf("tuic-%s-%d", s.uuid.String(), assocId), addr: addr} -} - func (s *serverHandler) handleStream() (err error) { for { var quicStream quic.Stream @@ -276,23 +271,6 @@ func (s *serverHandler) handleUniStream() (err error) { } } -type ServerAssocIdAddr struct { - assocId string - addr net.Addr -} - -func (a ServerAssocIdAddr) Network() string { - return "ServerAssocIdAddr" -} - -func (a ServerAssocIdAddr) String() string { - return a.assocId -} - -func (a ServerAssocIdAddr) RawAddr() net.Addr { - return a.addr -} - type serverUDPPacket struct { pc *quicStreamPacketConn packet *Packet From 13111081be154fa07708de7e59e406c763be5fc6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 12 Mar 2023 23:37:45 +0800 Subject: [PATCH 097/149] fix: SA4001 for net.UDPAddr copy --- tunnel/connection.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tunnel/connection.go b/tunnel/connection.go index bd8d1b63..e21bbdbf 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -46,7 +46,8 @@ func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, oAddr, } fromUDPAddr := from.(*net.UDPAddr) - fromUDPAddr = &(*fromUDPAddr) // make a copy + _fromUDPAddr := *fromUDPAddr + fromUDPAddr = &_fromUDPAddr // make a copy if fromAddr, ok := netip.AddrFromSlice(fromUDPAddr.IP); ok { if fAddr.IsValid() && (oAddr.Unmap() == fromAddr.Unmap()) { fromUDPAddr.IP = fAddr.Unmap().AsSlice() From 2f992e986391378bfc31779a23780362aeb7eb69 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:19:39 +0800 Subject: [PATCH 098/149] chore: fix issues #440 --- component/tls/utls.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/component/tls/utls.go b/component/tls/utls.go index e08ca7ee..857d598d 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -45,8 +45,13 @@ func GetFingerprint(ClientFingerprint string) (UClientHelloID, bool) { } fingerprint, ok := Fingerprints[ClientFingerprint] - log.Debugln("use specified fingerprint:%s", fingerprint.Client) - return fingerprint, ok + if ok { + log.Debugln("use specified fingerprint:%s", fingerprint.Client) + return fingerprint, ok + } else { + log.Warnln("wrong ClientFingerprint:%s", ClientFingerprint) + return UClientHelloID{}, false + } } func RollFingerprint() (UClientHelloID, bool) { @@ -128,10 +133,7 @@ func SetGlobalUtlsClient(Client string) { } func HaveGlobalFingerprint() bool { - if len(initUtlsClient) != 0 && initUtlsClient != "none" { - return true - } - return false + return len(initUtlsClient) != 0 && initUtlsClient != "none" } func GetGlobalFingerprint() string { From 2fef00d2a8f6bd7b54de52f1b2e3f6de2f84a85f Mon Sep 17 00:00:00 2001 From: 3andne <52860475+3andne@users.noreply.github.com> Date: Mon, 13 Mar 2023 22:33:24 -0700 Subject: [PATCH 099/149] Support Restls-V1 in Clash.Meta (#441) * feat: impl restls * fix: don't break shadowtls' working * chores: correct restls-client-go version * feat: bump restls-client-go version * fix: consistent naming `client-fingerprint` * docs: update example config * chore: adjust client-fingerprint's snippet --------- Co-authored-by: wwqgtxx Co-authored-by: Larvan2 <78135608+Larvan2@users.noreply.github.com> --- adapter/outbound/shadowsocks.go | 64 ++++++++++++++++++++++++--------- adapter/parser.go | 5 +++ docs/config.yaml | 40 ++++++++++++++++++++- go.mod | 1 + go.sum | 2 ++ transport/restls/restls.go | 57 +++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 transport/restls/restls.go diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index aab5cf42..49ff45ed 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -12,11 +12,13 @@ import ( "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/restls" obfs "github.com/Dreamacro/clash/transport/simple-obfs" shadowtls "github.com/Dreamacro/clash/transport/sing-shadowtls" "github.com/Dreamacro/clash/transport/socks5" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" + restlsC "github.com/3andne/restls-client-go" shadowsocks "github.com/metacubex/sing-shadowsocks" "github.com/metacubex/sing-shadowsocks/shadowimpl" "github.com/sagernet/sing/common/bufio" @@ -34,19 +36,21 @@ type ShadowSocks struct { obfsOption *simpleObfsOption v2rayOption *v2rayObfs.Option shadowTLSOption *shadowtls.ShadowTLSOption + restlsConfig *restlsC.Config } type ShadowSocksOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Password string `proxy:"password"` - Cipher string `proxy:"cipher"` - UDP bool `proxy:"udp,omitempty"` - Plugin string `proxy:"plugin,omitempty"` - PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` - UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Password string `proxy:"password"` + Cipher string `proxy:"cipher"` + UDP bool `proxy:"udp,omitempty"` + Plugin string `proxy:"plugin,omitempty"` + PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` + UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` + ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } type simpleObfsOption struct { @@ -66,12 +70,18 @@ type v2rayObfsOption struct { } type shadowTLSOption struct { - Password string `obfs:"password"` - Host string `obfs:"host"` - Fingerprint string `obfs:"fingerprint,omitempty"` - ClientFingerprint string `obfs:"client-fingerprint,omitempty"` - SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` - Version int `obfs:"version,omitempty"` + Password string `obfs:"password"` + Host string `obfs:"host"` + Fingerprint string `obfs:"fingerprint,omitempty"` + SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + Version int `obfs:"version,omitempty"` +} + +type restlsOption struct { + Password string `obfs:"password"` + Host string `obfs:"host"` + VersionHint string `obfs:"version-hint"` + RestlsScript string `obfs:"restls-script,omitempty"` } // StreamConn implements C.ProxyAdapter @@ -86,6 +96,7 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e if err != nil { return nil, err } + } return ss.streamConn(c, metadata) } @@ -103,6 +114,12 @@ func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } + case restls.Mode: + var err error + c, err = restls.NewRestls(c, ss.restlsConfig) + if err != nil { + return nil, fmt.Errorf("%s (restls) connect error: %w", ss.addr, err) + } } if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { if N.NeedHandshake(c) { @@ -202,6 +219,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { var v2rayOption *v2rayObfs.Option var obfsOption *simpleObfsOption var shadowTLSOpt *shadowtls.ShadowTLSOption + var restlsConfig *restlsC.Config obfsMode := "" decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) @@ -250,10 +268,23 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { Password: opt.Password, Host: opt.Host, Fingerprint: opt.Fingerprint, - ClientFingerprint: opt.ClientFingerprint, + ClientFingerprint: option.ClientFingerprint, SkipCertVerify: opt.SkipCertVerify, Version: opt.Version, } + } else if option.Plugin == restls.Mode { + obfsMode = restls.Mode + restlsOpt := &restlsOption{} + if err := decoder.Decode(option.PluginOpts, restlsOpt); err != nil { + return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) + } + + restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint) + restlsConfig.SessionTicketsDisabled = true + if err != nil { + return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err) + } + } return &ShadowSocks{ @@ -274,6 +305,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { v2rayOption: v2rayOption, obfsOption: obfsOption, shadowTLSOption: shadowTLSOpt, + restlsConfig: restlsConfig, }, nil } diff --git a/adapter/parser.go b/adapter/parser.go index d9c18694..65db74c4 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -24,6 +24,11 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { switch proxyType { case "ss": ssOption := &outbound.ShadowSocksOption{} + + if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { + ssOption.ClientFingerprint = GlobalUtlsClient + } + err = decoder.Decode(mapping, ssOption) if err != nil { break diff --git a/docs/config.yaml b/docs/config.yaml index e08a6e33..3ef09342 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -331,17 +331,55 @@ proxies: # socks5 # headers: # custom: value - - name: "ss4" + - name: "ss4-shadow-tls" type: ss server: server port: 443 cipher: chacha20-ietf-poly1305 password: "password" plugin: shadow-tls + client-fingerprint: chrome plugin-opts: host: "cloud.tencent.com" password: "shadow_tls_password" version: 2 # support 1/2/3 + + - name: "ss-restls-tls13" + type: ss + server: [YOUR_SERVER_IP] + port: 443 + cipher: chacha20-ietf-poly1305 + password: [YOUR_SS_PASSWORD] + client-fingerprint: chrome # One of: chrome, ios, firefox or safari + # 可以是chrome, ios, firefox, safari中的一个 + plugin: restls + plugin-opts: + host: "www.microsoft.com" # Must be a TLS 1.3 server + # 应当是一个TLS 1.3 服务器 + password: [YOUR_RESTLS_PASSWORD] + version-hint: "tls13" + # Control your post-handshake traffic through restls-script + # Hide proxy behaviors like "tls in tls". + # see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md + # 用restls剧本来控制握手后的行为,隐藏"tls in tls"等特征 + # 详情:https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md + restls-script: "300?100<1,400~100,350~100,600~100,300~200,300~100" + + - name: "ss-restls-tls12" + type: ss + server: [YOUR_SERVER_IP] + port: 443 + cipher: chacha20-ietf-poly1305 + password: [YOUR_SS_PASSWORD] + client-fingerprint: chrome # One of: chrome, ios, firefox or safari + # 可以是chrome, ios, firefox, safari中的一个 + plugin: restls + plugin-opts: + host: "vscode.dev" # Must be a TLS 1.2 server + # 应当是一个TLS 1.2 服务器 + password: [YOUR_RESTLS_PASSWORD] + version-hint: "tls12" + restls-script: "1000?100<1,500~100,350~100,600~100,400~200" # vmess # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none diff --git a/go.mod b/go.mod index bde63cc3..28760b44 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( ) require ( + github.com/3andne/restls-client-go v0.1.4 github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 7b24bc35..f9e584a2 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU= +github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= diff --git a/transport/restls/restls.go b/transport/restls/restls.go new file mode 100644 index 00000000..4b03b825 --- /dev/null +++ b/transport/restls/restls.go @@ -0,0 +1,57 @@ +package restls + +import ( + "net" + + tls "github.com/3andne/restls-client-go" +) + +const ( + Mode string = "restls" +) + +// Restls +type Restls struct { + net.Conn + firstPacketCache []byte + firstPacket bool +} + +func (r *Restls) Read(b []byte) (int, error) { + if err := r.Conn.(*tls.UConn).Handshake(); err != nil { + return 0, err + } + n, err := r.Conn.(*tls.UConn).Read(b) + return n, err +} + +func (r *Restls) Write(b []byte) (int, error) { + if r.firstPacket { + r.firstPacketCache = append([]byte(nil), b...) + r.firstPacket = false + return len(b), nil + } + if len(r.firstPacketCache) != 0 { + b = append(r.firstPacketCache, b...) + r.firstPacketCache = nil + } + n, err := r.Conn.(*tls.UConn).Write(b) + return n, err +} + +// NewRestls return a Restls Connection +func NewRestls(conn net.Conn, config *tls.Config) (net.Conn, error) { + if config != nil { + clientIDPtr := config.ClientID.Load() + if clientIDPtr != nil { + return &Restls{ + Conn: tls.UClient(conn, config, *clientIDPtr), + firstPacket: true, + }, nil + } + } + return &Restls{ + Conn: tls.UClient(conn, config, tls.HelloChrome_Auto), + firstPacket: true, + }, nil +} From f4251e58a5e933d1da1f0f9896085bd838388537 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:23:10 +0800 Subject: [PATCH 100/149] chore: clean up code --- config/config.go | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/config/config.go b/config/config.go index dfcb772e..61222af7 100644 --- a/config/config.go +++ b/config/config.go @@ -989,30 +989,27 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][ re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) for k, v := range nsPolicy { - if strings.Contains(k, "geosite:") { - subkeys := strings.Split(k, ":") - subkeys = subkeys[1:] - subkeys = strings.Split(subkeys[0], ",") - //log.Infoln("subkeys:%+v", subkeys) - for _, subkey := range subkeys { - newKey := "geosite:" + subkey - //log.Infoln("newKey:%+v", newKey) - updatedPolicy[newKey] = v - } - } else if re.MatchString(k) { - subkeys := strings.Split(k, ",") - //log.Infoln("subkeys:%+v", subkeys) - for _, subkey := range subkeys { - updatedPolicy[subkey] = v + if strings.Contains(k, ",") { + if strings.Contains(k, "geosite:") { + subkeys := strings.Split(k, ":") + subkeys = subkeys[1:] + subkeys = strings.Split(subkeys[0], ",") + for _, subkey := range subkeys { + newKey := "geosite:" + subkey + updatedPolicy[newKey] = v + } + } else if re.MatchString(k) { + subkeys := strings.Split(k, ",") + for _, subkey := range subkeys { + updatedPolicy[subkey] = v + } } } else { updatedPolicy[k] = v } } - //log.Infoln("updatedPolicy:%+v", updatedPolicy) for domain, server := range updatedPolicy { - servers, err := utils.ToStringSlice(server) if err != nil { return nil, err From 5816dc295517528dae6b7902114efd552509dd5a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 14 Mar 2023 16:50:27 +0800 Subject: [PATCH 101/149] chore: better restls --- adapter/outbound/shadowsocks.go | 45 +++++++++++++------------------ transport/restls/restls.go | 48 +++++++++++---------------------- 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 49ff45ed..af034472 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -86,22 +86,14 @@ type restlsOption struct { // StreamConn implements C.ProxyAdapter func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { - switch ss.obfsMode { - case shadowtls.Mode: - // fix tls handshake not timeout - ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) - defer cancel() - var err error - c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption) - if err != nil { - return nil, err - } - - } - return ss.streamConn(c, metadata) + // fix tls handshake not timeout + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + return ss.StreamConnContext(ctx, c, metadata) } -func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { +func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) { + useEarly := false switch ss.obfsMode { case "tls": c = obfs.NewTLSObfs(c, ss.obfsOption.Host) @@ -114,21 +106,30 @@ func (ss *ShadowSocks) streamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } + case shadowtls.Mode: + var err error + c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption) + if err != nil { + return nil, err + } + useEarly = true case restls.Mode: var err error - c, err = restls.NewRestls(c, ss.restlsConfig) + c, err = restls.NewRestls(ctx, c, ss.restlsConfig) if err != nil { return nil, fmt.Errorf("%s (restls) connect error: %w", ss.addr, err) } + useEarly = true } + useEarly = useEarly || N.NeedHandshake(c) if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { - if N.NeedHandshake(c) { + if useEarly { 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) { + if useEarly { return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil } else { return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) @@ -152,15 +153,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale safeConnClose(c, err) }(c) - switch ss.obfsMode { - case shadowtls.Mode: - c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption) - if err != nil { - return nil, err - } - } - - c, err = ss.streamConn(c, metadata) + c, err = ss.StreamConnContext(ctx, c, metadata) return NewConn(c, ss), err } diff --git a/transport/restls/restls.go b/transport/restls/restls.go index 4b03b825..0f3ba8ac 100644 --- a/transport/restls/restls.go +++ b/transport/restls/restls.go @@ -1,6 +1,7 @@ package restls import ( + "context" "net" tls "github.com/3andne/restls-client-go" @@ -10,48 +11,29 @@ const ( Mode string = "restls" ) -// Restls type Restls struct { - net.Conn - firstPacketCache []byte - firstPacket bool + *tls.UConn } -func (r *Restls) Read(b []byte) (int, error) { - if err := r.Conn.(*tls.UConn).Handshake(); err != nil { - return 0, err - } - n, err := r.Conn.(*tls.UConn).Read(b) - return n, err -} - -func (r *Restls) Write(b []byte) (int, error) { - if r.firstPacket { - r.firstPacketCache = append([]byte(nil), b...) - r.firstPacket = false - return len(b), nil - } - if len(r.firstPacketCache) != 0 { - b = append(r.firstPacketCache, b...) - r.firstPacketCache = nil - } - n, err := r.Conn.(*tls.UConn).Write(b) - return n, err +func (r *Restls) Upstream() any { + return r.UConn.NetConn() } // NewRestls return a Restls Connection -func NewRestls(conn net.Conn, config *tls.Config) (net.Conn, error) { +func NewRestls(ctx context.Context, conn net.Conn, config *tls.Config) (net.Conn, error) { + clientHellowID := tls.HelloChrome_Auto if config != nil { clientIDPtr := config.ClientID.Load() if clientIDPtr != nil { - return &Restls{ - Conn: tls.UClient(conn, config, *clientIDPtr), - firstPacket: true, - }, nil + clientHellowID = *clientIDPtr } } - return &Restls{ - Conn: tls.UClient(conn, config, tls.HelloChrome_Auto), - firstPacket: true, - }, nil + restls := &Restls{ + UConn: tls.UClient(conn, config, clientHellowID), + } + if err := restls.HandshakeContext(ctx); err != nil { + return nil, err + } + + return restls, nil } From 6ba82c6d850909b053f2367a64c27e541a53bc16 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 14 Mar 2023 17:45:37 +0800 Subject: [PATCH 102/149] chore: cleanup code --- adapter/parser.go | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/adapter/parser.go b/adapter/parser.go index 65db74c4..1f626f33 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -23,12 +23,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { ) switch proxyType { case "ss": - ssOption := &outbound.ShadowSocksOption{} - - if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { - ssOption.ClientFingerprint = GlobalUtlsClient - } - + ssOption := &outbound.ShadowSocksOption{ClientFingerprint: tlsC.GetGlobalFingerprint()} err = decoder.Decode(mapping, ssOption) if err != nil { break @@ -61,10 +56,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { Method: "GET", Path: []string{"/"}, }, - } - - if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { - vmessOption.ClientFingerprint = GlobalUtlsClient + ClientFingerprint: tlsC.GetGlobalFingerprint(), } err = decoder.Decode(mapping, vmessOption) @@ -73,12 +65,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { } proxy, err = outbound.NewVmess(*vmessOption) case "vless": - vlessOption := &outbound.VlessOption{} - - if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { - vlessOption.ClientFingerprint = GlobalUtlsClient - } - + vlessOption := &outbound.VlessOption{ClientFingerprint: tlsC.GetGlobalFingerprint()} err = decoder.Decode(mapping, vlessOption) if err != nil { break @@ -92,12 +79,7 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { } proxy, err = outbound.NewSnell(*snellOption) case "trojan": - trojanOption := &outbound.TrojanOption{} - - if GlobalUtlsClient := tlsC.GetGlobalFingerprint(); len(GlobalUtlsClient) != 0 { - trojanOption.ClientFingerprint = GlobalUtlsClient - } - + trojanOption := &outbound.TrojanOption{ClientFingerprint: tlsC.GetGlobalFingerprint()} err = decoder.Decode(mapping, trojanOption) if err != nil { break From 88116d90324b86f49b990daeabad3cc96bd6b690 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 14 Mar 2023 20:10:52 +0800 Subject: [PATCH 103/149] fix: optimize health check --- adapter/provider/healthcheck.go | 24 ++++++------------------ adapter/provider/provider.go | 2 +- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index 6f79cd05..d1624cad 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -34,16 +34,15 @@ type HealthCheck struct { func (hc *HealthCheck) process() { ticker := time.NewTicker(time.Duration(hc.interval) * time.Second) - - go func() { - time.Sleep(30 * time.Second) - hc.lazyCheck() - }() - for { select { case <-ticker.C: - hc.lazyCheck() + now := time.Now().Unix() + if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) { + hc.check() + } else { + log.Debugln("Skip once health check because we are lazy") + } case <-hc.done: ticker.Stop() return @@ -51,17 +50,6 @@ func (hc *HealthCheck) process() { } } -func (hc *HealthCheck) lazyCheck() bool { - now := time.Now().Unix() - if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) { - hc.check() - return true - } else { - log.Debugln("Skip once health check because we are lazy") - return false - } -} - func (hc *HealthCheck) setProxy(proxies []C.Proxy) { hc.proxies = proxies } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index e8bd7ed1..1ea346bf 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -99,7 +99,7 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { pp.proxies = proxies pp.healthCheck.setProxy(proxies) if pp.healthCheck.auto() { - defer func() { go pp.healthCheck.lazyCheck() }() + go pp.healthCheck.check() } } From 0f24c2f849a9ff87da61a4f39db3cec422bc8c80 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 14 Mar 2023 22:18:55 +0800 Subject: [PATCH 104/149] chore: add /restart to restful api --- hub/route/restart.go | 53 ++++++++++++++++++++++++++++++++++++++++++++ hub/route/server.go | 1 + 2 files changed, 54 insertions(+) create mode 100644 hub/route/restart.go diff --git a/hub/route/restart.go b/hub/route/restart.go new file mode 100644 index 00000000..196896a3 --- /dev/null +++ b/hub/route/restart.go @@ -0,0 +1,53 @@ +package route + +import ( + "fmt" + "net/http" + "os" + "os/exec" + "runtime" + "syscall" + + "github.com/Dreamacro/clash/log" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/render" +) + +func restartRouter() http.Handler { + r := chi.NewRouter() + r.Post("/", restart) + return r +} + +func restart(w http.ResponseWriter, r *http.Request) { + // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 + execPath, err := os.Executable() + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) + return + } + + if runtime.GOOS == "windows" { + cmd := exec.Command(execPath, os.Args[1:]...) + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Start() + if err != nil { + log.Fatalln("restarting:: %s", err) + } + + os.Exit(0) + } + + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + err = syscall.Exec(execPath, os.Args, os.Environ()) + if err != nil { + log.Fatalln("restarting: %s", err) + } + + render.JSON(w, r, render.M{"status": "ok"}) +} diff --git a/hub/route/server.go b/hub/route/server.go index 2ce4ba8c..054b1ad1 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -86,6 +86,7 @@ func Start(addr string, tlsAddr string, secret string, r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) + r.Mount("/restart", restartRouter()) }) if uiPath != "" { From 09c53e7cb7def6e627918f98e8d954d1e10afe14 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 14 Mar 2023 22:37:07 +0800 Subject: [PATCH 105/149] chore: Chore: adjust the loading order, and then load the resource at last --- constant/status.go | 9 ++++++++ hub/executor/executor.go | 48 +++++++++++++++++----------------------- tunnel/tunnel.go | 25 +++++++++++++++++++++ 3 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 constant/status.go diff --git a/constant/status.go b/constant/status.go new file mode 100644 index 00000000..4a784bfa --- /dev/null +++ b/constant/status.go @@ -0,0 +1,9 @@ +package constant + +type TunnelStatus uint8 + +const ( + TunnelSuspend TunnelStatus = iota + TunnelInner + TunnelRunning +) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 21a25ecd..1b2ec572 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -75,24 +75,38 @@ func ParseWithBytes(buf []byte) (*config.Config, error) { func ApplyConfig(cfg *config.Config, force bool) { mux.Lock() defer mux.Unlock() - preUpdateExperimental(cfg) + + tunnel.OnSuspend() + + CTLS.ResetCertificate() + for _, c := range cfg.TLS.CustomTrustCert { + if err := CTLS.AddCertificate(c); err != nil { + log.Warnln("%s\nadd error: %s", c, err.Error()) + } + } + updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) updateGeneral(cfg.General) - initInnerTcp() updateDNS(cfg.DNS, cfg.General.IPv6) - loadProxyProvider(cfg.Providers) - updateProfile(cfg) - loadRuleProvider(cfg.RuleProviders) updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) updateTun(cfg.General) updateExperimental(cfg) updateTunnels(cfg.Tunnels) + tunnel.OnInnerLoading() + + initInnerTcp() + loadProxyProvider(cfg.Providers) + updateProfile(cfg) + loadRuleProvider(cfg.RuleProviders) + + tunnel.OnRunning() + log.SetLevel(cfg.General.LogLevel) } @@ -144,10 +158,6 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList return } - if general.Interface == "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) { - dialer.DefaultInterface.Store(general.Interface) - } - allowLan := general.AllowLan listener.SetAllowLan(allowLan) @@ -168,15 +178,6 @@ func updateExperimental(c *config.Config) { runtime.GC() } -func preUpdateExperimental(c *config.Config) { - CTLS.ResetCertificate() - for _, c := range c.TLS.CustomTrustCert { - if err := CTLS.AddCertificate(c); err != nil { - log.Warnln("%s\nadd error: %s", c, err.Error()) - } - } -} - func updateDNS(c *config.DNS, generalIPv6 bool) { if !c.Enable { resolver.DefaultResolver = nil @@ -342,17 +343,8 @@ func updateGeneral(general *config.General) { inbound.SetTfo(general.InboundTfo) adapter.UnifiedDelay.Store(general.UnifiedDelay) - // Avoid reload configuration clean the value, causing traffic loops - if listener.GetTunConf().Enable && listener.GetTunConf().AutoDetectInterface { - // changed only when the name is specified - // if name is empty, setting delay until after tun loaded - if general.Interface != "" && (!general.Tun.Enable || !general.Tun.AutoDetectInterface) { - dialer.DefaultInterface.Store(general.Interface) - } - } else { - dialer.DefaultInterface.Store(general.Interface) - } + dialer.DefaultInterface.Store(general.Interface) dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) if general.RoutingMark > 0 { log.Infoln("Use routing mark: %#x", general.RoutingMark) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b686eae6..aad1dda7 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -26,6 +26,7 @@ import ( ) var ( + status C.TunnelStatus tcpQueue = make(chan C.ConnContext, 200) udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() @@ -49,6 +50,18 @@ var ( fakeIPRange netip.Prefix ) +func OnSuspend() { + status = C.TunnelSuspend +} + +func OnInnerLoading() { + status = C.TunnelInner +} + +func OnRunning() { + status = C.TunnelRunning +} + func SetFakeIPRange(p netip.Prefix) { fakeIPRange = p } @@ -158,10 +171,18 @@ func SetFindProcessMode(mode P.FindProcessMode) { findProcessMode = mode } +func isHandle(t C.Type) bool { + return status == C.TunnelRunning || (status == C.TunnelInner && t == C.INNER) +} + // processUDP starts a loop to handle udp packet func processUDP() { queue := udpQueue for conn := range queue { + if !isHandle(conn.Metadata().Type) { + conn.Drop() + continue + } handleUDPConn(conn) } } @@ -177,6 +198,10 @@ func process() { queue := tcpQueue for conn := range queue { + if !isHandle(conn.Metadata().Type) { + _ = conn.Conn().Close() + continue + } go handleTCPConn(conn) } } From 68d7a6da7f795045d9090016cb74ed16dd4fd43f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 14 Mar 2023 22:38:42 +0800 Subject: [PATCH 106/149] fix: ensure restart api return ok --- hub/route/restart.go | 49 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index 196896a3..bbf83f5e 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -21,7 +21,7 @@ func restartRouter() http.Handler { } func restart(w http.ResponseWriter, r *http.Request) { - // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 + // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 execPath, err := os.Executable() if err != nil { render.Status(r, http.StatusInternalServerError) @@ -29,25 +29,34 @@ func restart(w http.ResponseWriter, r *http.Request) { return } - if runtime.GOOS == "windows" { - cmd := exec.Command(execPath, os.Args[1:]...) - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - log.Fatalln("restarting:: %s", err) + render.JSON(w, r, render.M{"status": "ok"}) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + + // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 + // The background context is used because the underlying functions wrap it + // with timeout and shut down the server, which handles current request. It + // also should be done in a separate goroutine for the same reason. + go func() { + if runtime.GOOS == "windows" { + cmd := exec.Command(execPath, os.Args[1:]...) + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Start() + if err != nil { + log.Fatalln("restarting:: %s", err) + } + + os.Exit(0) } - os.Exit(0) - } - - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - err = syscall.Exec(execPath, os.Args, os.Environ()) - if err != nil { - log.Fatalln("restarting: %s", err) - } - - render.JSON(w, r, render.M{"status": "ok"}) + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + err = syscall.Exec(execPath, os.Args, os.Environ()) + if err != nil { + log.Fatalln("restarting: %s", err) + } + }() } From cf7520ec22d0c6bcbbfb365dfec96f3dbea7fb6f Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 14 Mar 2023 22:57:43 +0800 Subject: [PATCH 107/149] chore: disconnect when suspended --- tunnel/tunnel.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index aad1dda7..342cdd65 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -12,6 +12,7 @@ import ( "time" "github.com/jpillora/backoff" + "go.uber.org/atomic" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/nat" @@ -26,7 +27,7 @@ import ( ) var ( - status C.TunnelStatus + status = atomic.NewInt32(int32(C.TunnelSuspend)) tcpQueue = make(chan C.ConnContext, 200) udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() @@ -51,15 +52,18 @@ var ( ) func OnSuspend() { - status = C.TunnelSuspend + status.Store(int32(C.TunnelSuspend)) + for _, c := range statistic.DefaultManager.Snapshot().Connections { + _ = c.Close() + } } func OnInnerLoading() { - status = C.TunnelInner + status.Store(int32(C.TunnelInner)) } func OnRunning() { - status = C.TunnelRunning + status.Store(int32(C.TunnelRunning)) } func SetFakeIPRange(p netip.Prefix) { @@ -172,6 +176,7 @@ func SetFindProcessMode(mode P.FindProcessMode) { } func isHandle(t C.Type) bool { + status := C.TunnelStatus(status.Load()) return status == C.TunnelRunning || (status == C.TunnelInner && t == C.INNER) } From 8dda9fdb70beff793f41f5aa5a6f6629c726c1fa Mon Sep 17 00:00:00 2001 From: Skyxim Date: Tue, 14 Mar 2023 23:52:27 +0800 Subject: [PATCH 108/149] fix: The default interface is actually configured incorrectly --- config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config/config.go b/config/config.go index 61222af7..457ce72f 100644 --- a/config/config.go +++ b/config/config.go @@ -451,7 +451,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) } - dialer.DefaultInterface.Store(config.General.Interface) proxies, providers, err := parseProxies(rawCfg) if err != nil { return nil, err From 53928eb895f3437ed4338731f8e6909ba4f7b23e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 Mar 2023 00:10:54 +0800 Subject: [PATCH 109/149] chore: better TunnelStatus define --- constant/status.go | 9 ----- tunnel/status.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++ tunnel/tunnel.go | 17 +++++---- 3 files changed, 102 insertions(+), 16 deletions(-) delete mode 100644 constant/status.go create mode 100644 tunnel/status.go diff --git a/constant/status.go b/constant/status.go deleted file mode 100644 index 4a784bfa..00000000 --- a/constant/status.go +++ /dev/null @@ -1,9 +0,0 @@ -package constant - -type TunnelStatus uint8 - -const ( - TunnelSuspend TunnelStatus = iota - TunnelInner - TunnelRunning -) diff --git a/tunnel/status.go b/tunnel/status.go new file mode 100644 index 00000000..d81dd45e --- /dev/null +++ b/tunnel/status.go @@ -0,0 +1,92 @@ +package tunnel + +import ( + "encoding/json" + "errors" + "strings" + "sync/atomic" +) + +type TunnelStatus int + +// StatusMapping is a mapping for Status enum +var StatusMapping = map[string]TunnelStatus{ + Suspend.String(): Suspend, + Inner.String(): Inner, + Running.String(): Running, +} + +const ( + Suspend TunnelStatus = iota + Inner + Running +) + +// UnmarshalJSON unserialize Status +func (s *TunnelStatus) UnmarshalJSON(data []byte) error { + var tp string + json.Unmarshal(data, &tp) + status, exist := StatusMapping[strings.ToLower(tp)] + if !exist { + return errors.New("invalid mode") + } + *s = status + return nil +} + +// UnmarshalYAML unserialize Status with yaml +func (s *TunnelStatus) UnmarshalYAML(unmarshal func(any) error) error { + var tp string + unmarshal(&tp) + status, exist := StatusMapping[strings.ToLower(tp)] + if !exist { + return errors.New("invalid status") + } + *s = status + return nil +} + +// MarshalJSON serialize Status +func (s TunnelStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// MarshalYAML serialize TunnelMode with yaml +func (s TunnelStatus) MarshalYAML() (any, error) { + return s.String(), nil +} + +func (s TunnelStatus) String() string { + switch s { + case Suspend: + return "suspend" + case Inner: + return "inner" + case Running: + return "running" + default: + return "Unknown" + } +} + +type AtomicStatus struct { + value atomic.Int32 +} + +func (a *AtomicStatus) Store(s TunnelStatus) { + a.value.Store(int32(s)) +} + +func (a *AtomicStatus) Load() TunnelStatus { + return TunnelStatus(a.value.Load()) +} + +func (a *AtomicStatus) String() string { + return a.Load().String() +} + +func newAtomicStatus(s TunnelStatus) *AtomicStatus { + a := &AtomicStatus{} + a.Store(s) + return a +} diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 342cdd65..532c9e0e 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -12,7 +12,6 @@ import ( "time" "github.com/jpillora/backoff" - "go.uber.org/atomic" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/nat" @@ -27,7 +26,7 @@ import ( ) var ( - status = atomic.NewInt32(int32(C.TunnelSuspend)) + status = newAtomicStatus(Suspend) tcpQueue = make(chan C.ConnContext, 200) udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() @@ -52,18 +51,22 @@ var ( ) func OnSuspend() { - status.Store(int32(C.TunnelSuspend)) + status.Store(Suspend) for _, c := range statistic.DefaultManager.Snapshot().Connections { _ = c.Close() } } func OnInnerLoading() { - status.Store(int32(C.TunnelInner)) + status.Store(Inner) } func OnRunning() { - status.Store(int32(C.TunnelRunning)) + status.Store(Running) +} + +func Status() TunnelStatus { + return status.Load() } func SetFakeIPRange(p netip.Prefix) { @@ -176,8 +179,8 @@ func SetFindProcessMode(mode P.FindProcessMode) { } func isHandle(t C.Type) bool { - status := C.TunnelStatus(status.Load()) - return status == C.TunnelRunning || (status == C.TunnelInner && t == C.INNER) + status := status.Load() + return status == Running || (status == Inner && t == C.INNER) } // processUDP starts a loop to handle udp packet From 9d9bd245f3abed0c98c64ee25297277a598bd6b9 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 15 Mar 2023 09:05:16 +0800 Subject: [PATCH 110/149] fix: Adjust the timing of subscription information acquisition --- adapter/provider/provider.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 1ea346bf..4a2cf7b8 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -80,6 +80,7 @@ func (pp *proxySetProvider) Initial() error { return err } pp.OnUpdate(elm) + pp.getSubscriptionInfo() return nil } @@ -172,8 +173,6 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg), proxiesOnUpdate(pd)) pd.Fetcher = fetcher - - pd.getSubscriptionInfo() wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) return wrapper, nil From 5d0efb5472455e35d8a0c9b1e066333f98fb4712 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 15 Mar 2023 09:18:03 +0800 Subject: [PATCH 111/149] chore: keep existing connections --- tunnel/tunnel.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 532c9e0e..c4f55fbd 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -52,9 +52,6 @@ var ( func OnSuspend() { status.Store(Suspend) - for _, c := range statistic.DefaultManager.Snapshot().Connections { - _ = c.Close() - } } func OnInnerLoading() { From 516c219580f6abdec7fa9584fd8597d5f4d55ee6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 Mar 2023 09:55:48 +0800 Subject: [PATCH 112/149] fix: let quic-go works on outbound's packetConn --- adapter/outbound/base.go | 10 +++++++++- common/net/addr.go | 36 ++++++++++++++++++++++++++++++++++++ transport/tuic/conn.go | 24 +----------------------- transport/tuic/server.go | 3 ++- 4 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 common/net/addr.go diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index b69311f6..a9615be0 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -210,6 +210,8 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { type packetConn struct { net.PacketConn chain C.Chain + adapterName string + connID string actualRemoteDestination string } @@ -227,8 +229,14 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) { c.chain = append(c.chain, a.Name()) } +func (c *packetConn) LocalAddr() net.Addr { + lAddr := c.PacketConn.LocalAddr() + return N.NewCustomAddr(c.adapterName, c.connID, lAddr) // make quic-go's connMultiplexer happy +} + func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { - return &packetConn{pc, []string{a.Name()}, parseRemoteDestination(a.Addr())} + id, _ := utils.UnsafeUUIDGenerator.NewV4() + return &packetConn{pc, []string{a.Name()}, a.Name(), id.String(), parseRemoteDestination(a.Addr())} } func parseRemoteDestination(addr string) string { diff --git a/common/net/addr.go b/common/net/addr.go new file mode 100644 index 00000000..4efaefcd --- /dev/null +++ b/common/net/addr.go @@ -0,0 +1,36 @@ +package net + +import ( + "net" +) + +type CustomAddr interface { + net.Addr + RawAddr() net.Addr +} + +type customAddr struct { + networkStr string + addrStr string + rawAddr net.Addr +} + +func (a customAddr) Network() string { + return a.networkStr +} + +func (a customAddr) String() string { + return a.addrStr +} + +func (a customAddr) RawAddr() net.Addr { + return a.rawAddr +} + +func NewCustomAddr(networkStr string, addrStr string, rawAddr net.Addr) CustomAddr { + return customAddr{ + networkStr: networkStr, + addrStr: addrStr, + rawAddr: rawAddr, + } +} diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index ee687425..dfa43e1f 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -250,29 +250,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro } func (q *quicStreamPacketConn) LocalAddr() net.Addr { - addr := q.quicConn.LocalAddr() - if q.inputConn != nil { // client - return &packetAddr{addrStr: q.quicConn.LocalAddr().String(), connId: q.connId, rawAddr: addr} - } - return addr // server + return q.quicConn.LocalAddr() } var _ net.PacketConn = &quicStreamPacketConn{} - -type packetAddr struct { - addrStr string - connId uint32 - rawAddr net.Addr -} - -func (a packetAddr) Network() string { - return "tuic" -} - -func (a packetAddr) String() string { - return fmt.Sprintf("%s-%d", a.addrStr, a.connId) -} - -func (a packetAddr) RawAddr() net.Addr { - return a.rawAddr -} diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 770e1869..b23f3ae9 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "crypto/tls" + "fmt" "net" "sync" "sync/atomic" @@ -153,7 +154,7 @@ func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err err return s.HandleUdpFn(packet.ADDR.SocksAddr(), &serverUDPPacket{ pc: pc, packet: &packet, - rAddr: &packetAddr{addrStr: "tuic-" + s.uuid.String(), connId: assocId, rawAddr: s.quicConn.RemoteAddr()}, + rAddr: N.NewCustomAddr("tuic", fmt.Sprintf("tuic-%s-%d", s.uuid, assocId), s.quicConn.RemoteAddr()), // for tunnel's handleUDPConn }) } From f7610ce2ad7525be367bc43e584e609d8da3d121 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 Mar 2023 10:10:03 +0800 Subject: [PATCH 113/149] chore: better uuid using --- adapter/outbound/base.go | 10 ++-------- adapter/provider/healthcheck.go | 5 +---- common/convert/util.go | 3 +-- common/utils/uuid.go | 30 +++++++++++++++++++++++++++++- context/conn.go | 4 +--- context/dns.go | 3 +-- context/packetconn.go | 3 +-- transport/tuic/server.go | 6 +----- tunnel/statistic/tracker.go | 6 ++---- 9 files changed, 39 insertions(+), 31 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index a9615be0..156bc114 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -34,12 +34,7 @@ func (b *Base) Name() string { // Id implements C.ProxyAdapter func (b *Base) Id() string { if b.id == "" { - id, err := utils.UnsafeUUIDGenerator.NewV6() - if err != nil { - b.id = b.name - } else { - b.id = id.String() - } + b.id = utils.NewUUIDV6().String() } return b.id @@ -235,8 +230,7 @@ func (c *packetConn) LocalAddr() net.Addr { } func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { - id, _ := utils.UnsafeUUIDGenerator.NewV4() - return &packetConn{pc, []string{a.Name()}, a.Name(), id.String(), parseRemoteDestination(a.Addr())} + return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())} } func parseRemoteDestination(addr string) string { diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index d1624cad..9deb7b82 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -64,10 +64,7 @@ func (hc *HealthCheck) touch() { func (hc *HealthCheck) check() { _, _, _ = hc.singleDo.Do(func() (struct{}, error) { - id := "" - if uid, err := utils.UnsafeUUIDGenerator.NewV4(); err == nil { - id = uid.String() - } + id := utils.NewUUIDV4().String() log.Debugln("Start New Health Checking {%s}", id) b, _ := batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](10)) for _, proxy := range hc.proxies { diff --git a/common/convert/util.go b/common/convert/util.go index 4f86e616..0ec35acd 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -294,8 +294,7 @@ var ( ) func RandHost() string { - id, _ := utils.UnsafeUUIDGenerator.NewV4() - base := strings.ToLower(base64.RawURLEncoding.EncodeToString(id.Bytes())) + base := strings.ToLower(base64.RawURLEncoding.EncodeToString(utils.NewUUIDV4().Bytes())) base = strings.ReplaceAll(base, "-", "") base = strings.ReplaceAll(base, "_", "") buf := []byte(base) diff --git a/common/utils/uuid.go b/common/utils/uuid.go index d043ae37..930fda7d 100644 --- a/common/utils/uuid.go +++ b/common/utils/uuid.go @@ -13,11 +13,39 @@ func (r fastRandReader) Read(p []byte) (int, error) { var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{})) +func NewUUIDV1() uuid.UUID { + u, _ := UnsafeUUIDGenerator.NewV1() // fastrand.Read wouldn't cause error, so ignore err is safe + return u +} + +func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID { + return UnsafeUUIDGenerator.NewV3(ns, name) +} + +func NewUUIDV4() uuid.UUID { + u, _ := UnsafeUUIDGenerator.NewV4() // fastrand.Read wouldn't cause error, so ignore err is safe + return u +} + +func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID { + return UnsafeUUIDGenerator.NewV5(ns, name) +} + +func NewUUIDV6() uuid.UUID { + u, _ := UnsafeUUIDGenerator.NewV6() // fastrand.Read wouldn't cause error, so ignore err is safe + return u +} + +func NewUUIDV7() uuid.UUID { + u, _ := UnsafeUUIDGenerator.NewV7() // fastrand.Read wouldn't cause error, so ignore err is safe + return u +} + // UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090 func UUIDMap(str string) (uuid.UUID, error) { u, err := uuid.FromString(str) if err != nil { - return UnsafeUUIDGenerator.NewV5(uuid.Nil, str), nil + return NewUUIDV5(uuid.Nil, str), nil } return u, nil } diff --git a/context/conn.go b/context/conn.go index 35ea788d..c5477780 100644 --- a/context/conn.go +++ b/context/conn.go @@ -17,10 +17,8 @@ type ConnContext struct { } func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext { - id, _ := utils.UnsafeUUIDGenerator.NewV4() - return &ConnContext{ - id: id, + id: utils.NewUUIDV4(), metadata: metadata, conn: N.NewBufferedConn(conn), } diff --git a/context/dns.go b/context/dns.go index 80a4c988..c41de724 100644 --- a/context/dns.go +++ b/context/dns.go @@ -23,11 +23,10 @@ type DNSContext struct { } func NewDNSContext(ctx context.Context, msg *dns.Msg) *DNSContext { - id, _ := utils.UnsafeUUIDGenerator.NewV4() return &DNSContext{ Context: ctx, - id: id, + id: utils.NewUUIDV4(), msg: msg, } } diff --git a/context/packetconn.go b/context/packetconn.go index 87a9290a..b9afef41 100644 --- a/context/packetconn.go +++ b/context/packetconn.go @@ -16,9 +16,8 @@ type PacketConnContext struct { } func NewPacketConnContext(metadata *C.Metadata) *PacketConnContext { - id, _ := utils.UnsafeUUIDGenerator.NewV4() return &PacketConnContext{ - id: id, + id: utils.NewUUIDV4(), metadata: metadata, } } diff --git a/transport/tuic/server.go b/transport/tuic/server.go index b23f3ae9..5eb6e611 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -56,14 +56,10 @@ func (s *Server) Serve() error { return err } SetCongestionController(conn, s.CongestionController) - uuid, err := utils.UnsafeUUIDGenerator.NewV4() - if err != nil { - return err - } h := &serverHandler{ Server: s, quicConn: conn, - uuid: uuid, + uuid: utils.NewUUIDV4(), authCh: make(chan struct{}), } go h.handle() diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index bb5678b8..f7e9d971 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -83,7 +83,6 @@ func (tt *tcpTracker) Upstream() any { } func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *tcpTracker { - uuid, _ := utils.UnsafeUUIDGenerator.NewV4() if conn != nil { if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { metadata.RemoteDst = tcpAddr.IP.String() @@ -96,7 +95,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R Conn: conn, manager: manager, trackerInfo: &trackerInfo{ - UUID: uuid, + UUID: utils.NewUUIDV4(), Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), @@ -149,14 +148,13 @@ func (ut *udpTracker) Close() error { } func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *udpTracker { - uuid, _ := utils.UnsafeUUIDGenerator.NewV4() metadata.RemoteDst = conn.RemoteDestination() ut := &udpTracker{ PacketConn: conn, manager: manager, trackerInfo: &trackerInfo{ - UUID: uuid, + UUID: utils.NewUUIDV4(), Start: time.Now(), Metadata: metadata, Chain: conn.Chains(), From e8d4f8ae7b59473fe4db2c951baa2cd4627ec06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 15 Mar 2023 14:08:52 +0800 Subject: [PATCH 114/149] Update UoT protocol --- adapter/outbound/shadowsocks.go | 34 ++++++++++++++++++++++++++++----- go.mod | 4 ++-- go.sum | 10 ++++++---- listener/sing/sing.go | 14 +++++++++++--- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index af034472..c3097d09 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -50,6 +50,7 @@ type ShadowSocksOption struct { Plugin string `proxy:"plugin,omitempty"` PluginOpts map[string]any `proxy:"plugin-opts,omitempty"` UDPOverTCP bool `proxy:"udp-over-tcp,omitempty"` + UDPOverTCPVersion int `proxy:"udp-over-tcp-version,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } @@ -123,10 +124,16 @@ func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metada } useEarly = useEarly || N.NeedHandshake(c) if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { - if useEarly { - return ss.method.DialEarlyConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")), nil + var uotDestination M.Socksaddr + if ss.option.UDPOverTCPVersion == 1 { + uotDestination.Fqdn = uot.LegacyMagicAddress } else { - return ss.method.DialConn(c, M.ParseSocksaddr(uot.UOTMagicAddress+":443")) + uotDestination.Fqdn = uot.MagicAddress + } + if useEarly { + return ss.method.DialEarlyConn(c, uotDestination), nil + } else { + return ss.method.DialConn(c, uotDestination) } } if useEarly { @@ -169,7 +176,12 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial if err != nil { return nil, err } - return newPacketConn(uot.NewClientConn(tcpConn), ss), nil + destination := M.ParseSocksaddr(metadata.RemoteAddress()) + if ss.option.UDPOverTCPVersion == 1 { + return newPacketConn(uot.NewConn(tcpConn, false, destination), ss), nil + } else { + return newPacketConn(uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), ss), nil + } } addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) if err != nil { @@ -192,7 +204,12 @@ func (ss *ShadowSocks) SupportWithDialer() bool { // ListenPacketOnStreamConn implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { - return newPacketConn(uot.NewClientConn(c), ss), nil + destination := M.ParseSocksaddr(metadata.RemoteAddress()) + if ss.option.UDPOverTCPVersion == 1 { + return newPacketConn(uot.NewConn(c, false, destination), ss), nil + } else { + return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil + } } return nil, errors.New("no support") } @@ -279,6 +296,13 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } } + switch option.UDPOverTCPVersion { + case uot.Version, uot.LegacyVersion: + case 0: + option.UDPOverTCPVersion = uot.Version + default: + return nil, fmt.Errorf("ss %s unknown udp over tcp protocol version: %d", addr, option.UDPOverTCPVersion) + } return &ShadowSocks{ Base: &Base{ diff --git a/go.mod b/go.mod index 28760b44..c9080f7d 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( 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 + github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d @@ -44,7 +44,7 @@ require ( golang.org/x/exp v0.0.0-20221205204356-47842c84f3db golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.5.0 + golang.org/x/sys v0.6.0 google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.1.7 diff --git a/go.sum b/go.sum index f9e584a2..5fc1b7e0 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,10 @@ 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 h1:6DKo2FkSHn0nUcjO7bAext/ai7y7pCusK/+fScBJ5Jk= -github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.1.9-0.20230315055814-2f422b53b06f h1:bCx4+svKuZyw8CxxFXmrmhAERFf5BAAiNb1aYXj4hwk= +github.com/sagernet/sing v0.1.9-0.20230315055814-2f422b53b06f/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b h1:1iKGftQ59+shDSx2RaLaxXJcMK/B+IU9WqUPwyBW+E0= +github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvVDRpvv2F+RtECJN+VbuKjLtmQculQvOecls= @@ -222,8 +224,8 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index a3e15154..fa9e02f1 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -5,6 +5,7 @@ import ( "errors" "golang.org/x/exp/slices" "net" + "net/netip" "sync" "time" @@ -61,9 +62,16 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta switch metadata.Destination.Fqdn { case vmess.MuxDestination.Fqdn: return vmess.HandleMuxConnection(ctx, conn, h) - case uot.UOTMagicAddress: - metadata.Destination = M.Socksaddr{} - return h.NewPacketConnection(ctx, uot.NewClientConn(conn), metadata) + case uot.MagicAddress: + request, err := uot.ReadRequest(conn) + if err != nil { + return E.Cause(err, "read UoT request") + } + metadata.Destination = request.Destination + return h.NewPacketConnection(ctx, uot.NewConn(conn, request.IsConnect, metadata.Destination), metadata) + case uot.LegacyMagicAddress: + metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} + return h.NewPacketConnection(ctx, uot.NewConn(conn, false, metadata.Destination), metadata) } target := socks5.ParseAddr(metadata.Destination.String()) wg := &sync.WaitGroup{} From 7404bfdc445eeec6be8427f11d6239cb336e7909 Mon Sep 17 00:00:00 2001 From: H1JK Date: Wed, 15 Mar 2023 15:55:18 +0800 Subject: [PATCH 115/149] chore: Improve REALITY handshake --- component/tls/reality.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index a060de92..85309fb5 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -62,16 +62,12 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string } hello := uConn.HandshakeState.Hello - hello.SessionId = make([]byte, 32) + for i := range hello.SessionId { // https://github.com/golang/go/issues/5373 + hello.SessionId[i] = 0 + } copy(hello.Raw[39:], hello.SessionId) - var nowTime time.Time - if uConfig.Time != nil { - nowTime = uConfig.Time() - } else { - nowTime = time.Now() - } - binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix())) + binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix())) hello.SessionId[0] = 1 hello.SessionId[1] = 7 @@ -130,7 +126,7 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C return } //_, _ = io.Copy(io.Discard, response.Body) - time.Sleep(time.Duration(5 + fastrand.Int63n(10))) + time.Sleep(time.Duration(5+fastrand.Int63n(10)) * time.Second) response.Body.Close() client.CloseIdleConnections() } From 520cc807d6283ac751af45b96e401c3a0ad975fd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 Mar 2023 18:58:59 +0800 Subject: [PATCH 116/149] chore: Update dependencies --- go.mod | 22 +++++++++++----------- go.sum | 47 ++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index c9080f7d..2404ff99 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Dreamacro/clash go 1.19 require ( + github.com/3andne/restls-client-go v0.1.4 github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 @@ -20,16 +21,16 @@ require ( github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.1 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 - github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3 + github.com/metacubex/sing-tun v0.1.2 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb - github.com/miekg/dns v1.1.50 + github.com/miekg/dns v1.1.52 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.9-0.20230315063014-2731df16725b github.com/sagernet/sing-shadowtls v0.1.0 - github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc - github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d + github.com/sagernet/sing-vmess v0.1.3 + github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/samber/lo v1.37.0 @@ -40,9 +41,9 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.6.0 + golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/net v0.7.0 + golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.6.0 google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d @@ -51,7 +52,6 @@ require ( ) require ( - github.com/3andne/restls-client-go v0.1.4 github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -65,7 +65,7 @@ require ( github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005 // indirect + github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -75,10 +75,10 @@ require ( github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.org/x/tools v0.5.0 // indirect + golang.org/x/tools v0.6.0 // indirect ) replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 diff --git a/go.sum b/go.sum index 5fc1b7e0..64264a0f 100644 --- a/go.sum +++ b/go.sum @@ -89,20 +89,20 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005 h1:0TEvReK/D6YLszjGj/bdx4d7amQSjQ2X/98r4ZiUbxU= -github.com/metacubex/gvisor v0.0.0-20230304153416-e2bb9c726005/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= +github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be h1:zg8lXHo8t+dCSPHQ/wCJui1V+eO9TSh9NoIjKNvUykA= +github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.1 h1:ZIxZFGivpSLOEZuuNkLy+aPvo1RP4uRBjNg3SAkXwIg= github.com/metacubex/quic-go v0.33.1/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= 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.20230304153753-5058534177f3 h1:oQLThm1a8E7hHmoM9XF2cO0FZPsHVynC4YXW4b3liUI= -github.com/metacubex/sing-tun v0.1.1-0.20230304153753-5058534177f3/go.mod h1:b/19bRRhwampNPV+1gVDyDsQHmuGDaplxPQkwJh1kj4= +github.com/metacubex/sing-tun v0.1.2 h1:rQzy+11rt2ZCpCNIsFab5lWoYDTqkdaurofHo8f97yU= +github.com/metacubex/sing-tun v0.1.2/go.mod h1:+2JxFqCjgSmeeTygZjZSsQbTQUUVXwC3mxnASTs/EhU= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= +github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo= github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= @@ -127,16 +127,14 @@ 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.9-0.20230315055814-2f422b53b06f h1:bCx4+svKuZyw8CxxFXmrmhAERFf5BAAiNb1aYXj4hwk= -github.com/sagernet/sing v0.1.9-0.20230315055814-2f422b53b06f/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b h1:1iKGftQ59+shDSx2RaLaxXJcMK/B+IU9WqUPwyBW+E0= github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= -github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvVDRpvv2F+RtECJN+VbuKjLtmQculQvOecls= -github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc/go.mod h1:V14iffGwhZPU2S7wgIiPlLWXygSjAXazYzD1w0ejBl4= -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/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= +github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= +github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= +github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= @@ -173,15 +171,15 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -193,9 +191,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -217,9 +214,7 @@ golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -229,9 +224,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -239,9 +233,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 998d407d445c842ed78521c16c047ded28e3fa44 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 15 Mar 2023 23:43:58 +0800 Subject: [PATCH 117/149] Feat: support set tun file-descriptor in config file Co-authored-by: DuFoxit --- config/config.go | 2 ++ hub/route/configs.go | 4 ++++ listener/config/tun.go | 1 + listener/inbound/tun.go | 2 ++ listener/listener.go | 3 ++- listener/sing_tun/server.go | 1 + 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 457ce72f..c407aad5 100644 --- a/config/config.go +++ b/config/config.go @@ -218,6 +218,7 @@ type RawTun struct { ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` } type RawTuicServer struct { @@ -1239,6 +1240,7 @@ func parseTun(rawTun RawTun, general *General) error { ExcludePackage: rawTun.ExcludePackage, EndpointIndependentNat: rawTun.EndpointIndependentNat, UDPTimeout: rawTun.UDPTimeout, + FileDescriptor: rawTun.FileDescriptor, } return nil diff --git a/hub/route/configs.go b/hub/route/configs.go index 50e3cd13..afafe80e 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -80,6 +80,7 @@ type tunSchema struct { ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"` } type tuicServerSchema struct { @@ -169,6 +170,9 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.UDPTimeout != nil { def.UDPTimeout = *p.UDPTimeout } + if p.FileDescriptor != nil { + def.FileDescriptor = *p.FileDescriptor + } } return def } diff --git a/listener/config/tun.go b/listener/config/tun.go index 2e1d1a71..50f5cf7d 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -95,4 +95,5 @@ type Tun struct { ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index ad215989..eb16d2dd 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -33,6 +33,7 @@ type TunOption struct { ExcludePackage []string `inbound:"exclude_package,omitempty"` EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `inbound:"udp_timeout,omitempty"` + FileDescriptor int `inbound:"file-descriptor,omitempty"` } func (o TunOption) Equal(config C.InboundConfig) bool { @@ -96,6 +97,7 @@ func NewTun(options *TunOption) (*Tun, error) { ExcludePackage: options.ExcludePackage, EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: options.UDPTimeout, + FileDescriptor: options.FileDescriptor, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index d8eb5c0c..21dee51e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -821,7 +821,8 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { LastTunConf.MTU != tunConf.MTU || LastTunConf.StrictRoute != tunConf.StrictRoute || LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || - LastTunConf.UDPTimeout != tunConf.UDPTimeout { + LastTunConf.UDPTimeout != tunConf.UDPTimeout || + LastTunConf.FileDescriptor != tunConf.FileDescriptor { return true } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 06215c73..d7f42c98 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -212,6 +212,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte IncludeAndroidUser: options.IncludeAndroidUser, IncludePackage: options.IncludePackage, ExcludePackage: options.ExcludePackage, + FileDescriptor: options.FileDescriptor, InterfaceMonitor: defaultInterfaceMonitor, TableIndex: 2022, } From 3ae428570208b789030fce58b5872fe9738aaf74 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 16 Mar 2023 21:09:44 +0800 Subject: [PATCH 118/149] fix: tuic udp native mode can't relay packetSize>1200 --- adapter/outbound/tuic.go | 31 +++++++++++++++++++++---------- go.mod | 2 +- go.sum | 4 ++-- listener/config/tuic.go | 1 + listener/tuic/server.go | 10 ++++++++++ transport/tuic/conn.go | 3 +-- transport/tuic/protocol.go | 2 ++ 7 files changed, 38 insertions(+), 15 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index b6335fa8..d2f2b5e9 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -42,16 +42,17 @@ type TuicOption struct { DisableSni bool `proxy:"disable-sni,omitempty"` MaxUdpRelayPacketSize int `proxy:"max-udp-relay-packet-size,omitempty"` - FastOpen bool `proxy:"fast-open,omitempty"` - MaxOpenStreams int `proxy:"max-open-streams,omitempty"` - SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` - Fingerprint string `proxy:"fingerprint,omitempty"` - CustomCA string `proxy:"ca,omitempty"` - CustomCAString string `proxy:"ca-str,omitempty"` - ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` - ReceiveWindow int `proxy:"recv-window,omitempty"` - DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` - SNI string `proxy:"sni,omitempty"` + FastOpen bool `proxy:"fast-open,omitempty"` + MaxOpenStreams int `proxy:"max-open-streams,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + CustomCA string `proxy:"ca,omitempty"` + CustomCAString string `proxy:"ca-str,omitempty"` + ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` + ReceiveWindow int `proxy:"recv-window,omitempty"` + DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` + MaxDatagramFrameSize int `proxy:"max-datagram-frame-size,omitempty"` + SNI string `proxy:"sni,omitempty"` } // DialContext implements C.ProxyAdapter @@ -175,6 +176,15 @@ func NewTuic(option TuicOption) (*Tuic, error) { option.MaxOpenStreams = 100 } + if option.MaxDatagramFrameSize == 0 { + option.MaxDatagramFrameSize = option.MaxUdpRelayPacketSize + tuic.PacketOverHead + } + + if option.MaxDatagramFrameSize > 1400 { + option.MaxDatagramFrameSize = 1400 + } + option.MaxUdpRelayPacketSize = option.MaxDatagramFrameSize - tuic.PacketOverHead + // ensure server's incoming stream can handle correctly, increase to 1.1x quicMaxOpenStreams := int64(option.MaxOpenStreams) quicMaxOpenStreams = quicMaxOpenStreams + int64(math.Ceil(float64(quicMaxOpenStreams)/10.0)) @@ -187,6 +197,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { MaxIncomingUniStreams: quicMaxOpenStreams, KeepAlivePeriod: time.Duration(option.HeartbeatInterval) * time.Millisecond, DisablePathMTUDiscovery: option.DisableMTUDiscovery, + MaxDatagramFrameSize: int64(option.MaxDatagramFrameSize), EnableDatagrams: true, } if option.ReceiveWindowConn == 0 { diff --git a/go.mod b/go.mod index 2404ff99..0f44f260 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/jpillora/backoff v1.0.0 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.33.1 + github.com/metacubex/quic-go v0.33.2 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 github.com/metacubex/sing-tun v0.1.2 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb diff --git a/go.sum b/go.sum index 64264a0f..881d2b70 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be h1:zg8lXHo8t+dCSPHQ/wCJui1V+eO9TSh9NoIjKNvUykA= github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= -github.com/metacubex/quic-go v0.33.1 h1:ZIxZFGivpSLOEZuuNkLy+aPvo1RP4uRBjNg3SAkXwIg= -github.com/metacubex/quic-go v0.33.1/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= +github.com/metacubex/quic-go v0.33.2 h1:DsDdTaLvGI0eVV0C/jzPrw5MBwK5VR20r5Mt9uU5Djw= +github.com/metacubex/quic-go v0.33.2/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= 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.2 h1:rQzy+11rt2ZCpCNIsFab5lWoYDTqkdaurofHo8f97yU= diff --git a/listener/config/tuic.go b/listener/config/tuic.go index c584bbf5..991a04c9 100644 --- a/listener/config/tuic.go +++ b/listener/config/tuic.go @@ -15,6 +15,7 @@ type TuicServer struct { AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` ALPN []string `yaml:"alpn" json:"alpn,omitempty"` MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` + MaxDatagramFrameSize int `yaml:"max-datagram-frame-size" json:"max-datagram-frame-size,omitempty"` } func (t TuicServer) String() string { diff --git a/listener/tuic/server.go b/listener/tuic/server.go index a7ad69f6..92cc0b37 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -61,6 +61,16 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet quicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10 quicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow + if config.MaxUdpRelayPacketSize == 0 { + config.MaxUdpRelayPacketSize = 1500 + } + maxDatagramFrameSize := config.MaxUdpRelayPacketSize + tuic.PacketOverHead + if maxDatagramFrameSize > 1400 { + maxDatagramFrameSize = 1400 + } + config.MaxUdpRelayPacketSize = maxDatagramFrameSize - tuic.PacketOverHead + quicConfig.MaxDatagramFrameSize = int64(maxDatagramFrameSize) + tokens := make([][32]byte, len(config.Token)) for i, token := range config.Token { tokens[i] = tuic.GenTKN(token) diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index dfa43e1f..d5955e13 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -1,7 +1,6 @@ package tuic import ( - "fmt" "net" "net/netip" "sync" @@ -201,7 +200,7 @@ func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { if q.udpRelayMode != "quic" && len(p) > q.maxUdpRelayPacketSize { - return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.maxUdpRelayPacketSize) + return 0, quic.ErrMessageTooLarge(q.maxUdpRelayPacketSize) } if q.closed { return 0, net.ErrClosed diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index b14074a7..570b6e54 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -282,6 +282,8 @@ func (c Packet) BytesLen() int { return c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA) } +var PacketOverHead = NewPacket(0, 0, NewAddressAddrPort(netip.AddrPortFrom(netip.IPv6Unspecified(), 0)), nil).BytesLen() + type Dissociate struct { CommandHead ASSOC_ID uint32 From 8cb67b6480649edfa45dcc9ac89ce0789651e8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 17 Mar 2023 13:23:37 +0800 Subject: [PATCH 119/149] Update UoT protocol --- adapter/outbound/shadowsocks.go | 13 ++++--------- go.mod | 2 +- go.sum | 2 ++ listener/sing/sing.go | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index c3097d09..1c64b3ca 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -124,12 +124,7 @@ func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metada } useEarly = useEarly || N.NeedHandshake(c) if metadata.NetWork == C.UDP && ss.option.UDPOverTCP { - var uotDestination M.Socksaddr - if ss.option.UDPOverTCPVersion == 1 { - uotDestination.Fqdn = uot.LegacyMagicAddress - } else { - uotDestination.Fqdn = uot.MagicAddress - } + uotDestination := uot.RequestDestination(uint8(ss.option.UDPOverTCPVersion)) if useEarly { return ss.method.DialEarlyConn(c, uotDestination), nil } else { @@ -178,7 +173,7 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial } destination := M.ParseSocksaddr(metadata.RemoteAddress()) if ss.option.UDPOverTCPVersion == 1 { - return newPacketConn(uot.NewConn(tcpConn, false, destination), ss), nil + return newPacketConn(uot.NewConn(tcpConn, uot.Request{Destination: destination}), ss), nil } else { return newPacketConn(uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), ss), nil } @@ -205,8 +200,8 @@ func (ss *ShadowSocks) SupportWithDialer() bool { func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { destination := M.ParseSocksaddr(metadata.RemoteAddress()) - if ss.option.UDPOverTCPVersion == 1 { - return newPacketConn(uot.NewConn(c, false, destination), ss), nil + if ss.option.UDPOverTCPVersion == uot.LegacyVersion { + return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), ss), nil } else { return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil } diff --git a/go.mod b/go.mod index 0f44f260..a9967400 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( 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.9-0.20230315063014-2731df16725b + github.com/sagernet/sing v0.2.0 github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index 881d2b70..eeb4e912 100644 --- a/go.sum +++ b/go.sum @@ -129,6 +129,8 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b h1:1iKGftQ59+shDSx2RaLaxXJcMK/B+IU9WqUPwyBW+E0= github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing v0.2.0 h1:iyc4TaeXG5XYXixl48zSDDTw46C9NOEAVFq6ZE0dA2k= +github.com/sagernet/sing v0.2.0/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index fa9e02f1..9436fcfb 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -68,10 +68,10 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta return E.Cause(err, "read UoT request") } metadata.Destination = request.Destination - return h.NewPacketConnection(ctx, uot.NewConn(conn, request.IsConnect, metadata.Destination), metadata) + return h.NewPacketConnection(ctx, uot.NewConn(conn, *request), metadata) case uot.LegacyMagicAddress: metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} - return h.NewPacketConnection(ctx, uot.NewConn(conn, false, metadata.Destination), metadata) + return h.NewPacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata) } target := socks5.ParseAddr(metadata.Destination.String()) wg := &sync.WaitGroup{} From c7362fce9c6e147f2e011a40a044476e621fe4f1 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:49:42 +0800 Subject: [PATCH 120/149] chore: do not modify ALPN in utls --- component/tls/reality.go | 1 - component/tls/utls.go | 1 - 2 files changed, 2 deletions(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index 85309fb5..3dadb7fd 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -44,7 +44,6 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string } uConfig := &utls.Config{ ServerName: tlsConfig.ServerName, - NextProtos: tlsConfig.NextProtos, InsecureSkipVerify: true, SessionTicketsDisabled: true, VerifyPeerCertificate: verifier.VerifyPeerCertificate, diff --git a/component/tls/utls.go b/component/tls/utls.go index 857d598d..7ea2ad06 100644 --- a/component/tls/utls.go +++ b/component/tls/utls.go @@ -94,7 +94,6 @@ func copyConfig(c *tls.Config) *utls.Config { return &utls.Config{ RootCAs: c.RootCAs, ServerName: c.ServerName, - NextProtos: c.NextProtos, InsecureSkipVerify: c.InsecureSkipVerify, VerifyPeerCertificate: c.VerifyPeerCertificate, } From 84967ace53acca0fa9fc59e28dbe67e566603ce9 Mon Sep 17 00:00:00 2001 From: Phie Ash <56054933+yaoshiu@users.noreply.github.com> Date: Sat, 18 Mar 2023 19:55:29 +0800 Subject: [PATCH 121/149] Update flake.nix (#452) --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 88a6eacb..ffd18629 100644 --- a/flake.nix +++ b/flake.nix @@ -28,7 +28,7 @@ inherit version; src = ./.; - vendorSha256 = "sha256-8cbcE9gKJjU14DNTLPc6nneEPZg7Akt+FlSDlPRvG5k="; + vendorSha256 = "sha256-W5oiPtTRin0731QQWr98xZ2Vpk97HYcBtKoi1OKZz+w="; # Do not build testing suit excludedPackages = [ "./test" ]; From f6f02bb5ebb950b9b0a07fd367af14ac5e7b5c4a Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 18 Mar 2023 22:20:31 +0800 Subject: [PATCH 122/149] fix: ToLower first --- constant/path.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/constant/path.go b/constant/path.go index 29ac9872..707c53f6 100644 --- a/constant/path.go +++ b/constant/path.go @@ -66,7 +66,7 @@ func (p *path) MMDB() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(fi.Name(), "Country.mmdb") { + if strings.EqualFold(strings.ToLower(fi.Name()), "country.mmdb") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } @@ -93,7 +93,7 @@ func (p *path) GeoIP() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(fi.Name(), "GeoIP.dat") { + if strings.EqualFold(strings.ToLower(fi.Name()), "geoip.dat") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } @@ -112,7 +112,7 @@ func (p *path) GeoSite() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(fi.Name(), "GeoSite.dat") { + if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") { GeositeName = fi.Name() return P.Join(p.homeDir, fi.Name()) } From 9316c1293e01ad82492e50a80f3f4c58b2da8cfd Mon Sep 17 00:00:00 2001 From: metacubex Date: Sat, 18 Mar 2023 22:33:39 +0800 Subject: [PATCH 123/149] fix: geosite of nameserver-policy cannot be loaded correctly --- dns/filters.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dns/filters.go b/dns/filters.go index b51e6402..11c85c2c 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -92,6 +92,10 @@ type geoSiteFilter struct { } func NewGeoSite(group string) (fallbackDomainFilter, error) { + if err := geodata.InitGeoSite(); err != nil { + log.Errorln("can't initial GeoSite: %s", err) + return nil, err + } matcher, _, err := geodata.LoadGeoSiteMatcher(group) if err != nil { return nil, err From 3e47bfacf067a5a1b9ef3b3f90e7f305a7b62b91 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sun, 19 Mar 2023 17:31:52 +0800 Subject: [PATCH 124/149] feat: Converter support REALITY share standard --- common/convert/v.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/convert/v.go b/common/convert/v.go index 606d8aff..7e365170 100644 --- a/common/convert/v.go +++ b/common/convert/v.go @@ -38,6 +38,12 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m if sni := query.Get("sni"); sni != "" { proxy["servername"] = sni } + if realityPublicKey := query.Get("pbk"); realityPublicKey != "" { + proxy["reality-opts"] = map[string]any{ + "public-key": realityPublicKey, + "short-id": query.Get("sid"), + } + } switch query.Get("packetEncoding") { case "none": From 154fbb34eace2f1568f034d004f27973f6fa5d0a Mon Sep 17 00:00:00 2001 From: metacubex Date: Tue, 21 Mar 2023 00:45:25 +0800 Subject: [PATCH 125/149] fix: log typo --- component/geodata/memconservative/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/geodata/memconservative/cache.go b/component/geodata/memconservative/cache.go index 28c2c238..ca78d19d 100644 --- a/component/geodata/memconservative/cache.go +++ b/component/geodata/memconservative/cache.go @@ -118,7 +118,7 @@ func (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error) case errFailedToReadBytes, errFailedToReadExpectedLenBytes, errInvalidGeodataFile, errInvalidGeodataVarintLength: - log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method") + log.Warnln("failed to decode geosite file: %s%s", filename, ", fallback to the original ReadFile method") geositeBytes, err = os.ReadFile(asset) if err != nil { return nil, err From 0336435ebc4346597a2d571999f780dd98418145 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 Mar 2023 12:40:36 +0800 Subject: [PATCH 126/149] chore: shadowsocks listener support the "udp" setting --- listener/config/shadowsocks.go | 1 + listener/inbound/shadowsocks.go | 2 + listener/listener.go | 1 + listener/parse.go | 2 +- listener/shadowsocks/tcp.go | 12 +++--- listener/sing_shadowsocks/server.go | 62 +++++++++++++++-------------- 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/listener/config/shadowsocks.go b/listener/config/shadowsocks.go index cfe31f62..60540bbd 100644 --- a/listener/config/shadowsocks.go +++ b/listener/config/shadowsocks.go @@ -9,6 +9,7 @@ type ShadowsocksServer struct { Listen string Password string Cipher string + Udp bool } func (t ShadowsocksServer) String() string { diff --git a/listener/inbound/shadowsocks.go b/listener/inbound/shadowsocks.go index 40907485..4659f4d7 100644 --- a/listener/inbound/shadowsocks.go +++ b/listener/inbound/shadowsocks.go @@ -11,6 +11,7 @@ type ShadowSocksOption struct { BaseOption Password string `inbound:"password"` Cipher string `inbound:"cipher"` + UDP bool `inbound:"udp,omitempty"` } func (o ShadowSocksOption) Equal(config C.InboundConfig) bool { @@ -37,6 +38,7 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { Listen: base.RawAddress(), Password: options.Password, Cipher: options.Cipher, + Udp: options.UDP, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index 21dee51e..8f0088db 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -271,6 +271,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u Listen: addr, Password: password, Cipher: cipher, + Udp: true, } } diff --git a/listener/parse.go b/listener/parse.go index aa9e39ac..c8e1ddf7 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -73,7 +73,7 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { } listener, err = IN.NewTun(tunOption) case "shadowsocks": - shadowsocksOption := &IN.ShadowSocksOption{} + shadowsocksOption := &IN.ShadowSocksOption{UDP: true} err = decoder.Decode(mapping, shadowsocksOption) if err != nil { return nil, err diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index 21db5b63..c0fd490f 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -33,12 +33,14 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C for _, addr := range strings.Split(config.Listen, ",") { addr := addr - //UDP - ul, err := NewUDP(addr, pickCipher, udpIn) - if err != nil { - return nil, err + if config.Udp { + //UDP + ul, err := NewUDP(addr, pickCipher, udpIn) + if err != nil { + return nil, err + } + sl.udpListeners = append(sl.udpListeners, ul) } - sl.udpListeners = append(sl.udpListeners, ul) //TCP l, err := inbound.Listen("tcp", addr) diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 5602a3ed..c7e05bb5 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -76,37 +76,39 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C for _, addr := range strings.Split(config.Listen, ",") { addr := addr - //UDP - ul, err := net.ListenPacket("udp", addr) - if err != nil { - return nil, err - } - - err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) - if err != nil { - log.Warnln("Failed to Reuse UDP Address: %s", err) - } - - sl.udpListeners = append(sl.udpListeners, ul) - - go func() { - conn := bufio.NewPacketConn(ul) - for { - buff := buf.NewPacket() - remoteAddr, err := conn.ReadPacket(buff) - if err != nil { - buff.Release() - if sl.closed { - break - } - continue - } - _ = sl.service.NewPacket(context.TODO(), conn, buff, metadata.Metadata{ - Protocol: "shadowsocks", - Source: remoteAddr, - }) + if config.Udp { + //UDP + ul, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err } - }() + + err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) + if err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + sl.udpListeners = append(sl.udpListeners, ul) + + go func() { + conn := bufio.NewPacketConn(ul) + for { + buff := buf.NewPacket() + remoteAddr, err := conn.ReadPacket(buff) + if err != nil { + buff.Release() + if sl.closed { + break + } + continue + } + _ = sl.service.NewPacket(context.TODO(), conn, buff, metadata.Metadata{ + Protocol: "shadowsocks", + Source: remoteAddr, + }) + } + }() + } //TCP l, err := inbound.Listen("tcp", addr) From a22000c41b9c0f0acc2adb3bdfed752095be7ea1 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 21 Mar 2023 23:56:40 +0800 Subject: [PATCH 127/149] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cdfa3505..2e485db2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ ## Wiki -Documentation and configuring examples are available on [wiki](https://github.com/MetaCubeX/Clash.Meta/wiki) and [Clash.Meta Wiki](https://docs.metacubex.one/). +Documentation and configuring examples are available on [Clash.Meta Wiki](https://clash-meta.wiki). ## Build From e7bb1f42b1bff18493d0aa4509cb4f8b3e5c847d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 Mar 2023 13:01:45 +0800 Subject: [PATCH 128/149] chore: update quic-go to release unused buffer when error --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index a9967400..661adc9a 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/jpillora/backoff v1.0.0 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.33.2 + github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 github.com/metacubex/sing-tun v0.1.2 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb diff --git a/go.sum b/go.sum index eeb4e912..6f3236f2 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be h1:zg8lXHo8t+dCSPHQ/wCJui1V+eO9TSh9NoIjKNvUykA= github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= -github.com/metacubex/quic-go v0.33.2 h1:DsDdTaLvGI0eVV0C/jzPrw5MBwK5VR20r5Mt9uU5Djw= -github.com/metacubex/quic-go v0.33.2/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= +github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= +github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= 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.2 h1:rQzy+11rt2ZCpCNIsFab5lWoYDTqkdaurofHo8f97yU= @@ -127,8 +127,6 @@ 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.9-0.20230315063014-2731df16725b h1:1iKGftQ59+shDSx2RaLaxXJcMK/B+IU9WqUPwyBW+E0= -github.com/sagernet/sing v0.1.9-0.20230315063014-2731df16725b/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing v0.2.0 h1:iyc4TaeXG5XYXixl48zSDDTw46C9NOEAVFq6ZE0dA2k= github.com/sagernet/sing v0.2.0/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= From e026ac6a2a43459e964588f14a5f7d564e568297 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 22 Mar 2023 23:45:26 +0800 Subject: [PATCH 129/149] chore: update xray-core version --- component/tls/reality.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/tls/reality.go b/component/tls/reality.go index 3dadb7fd..dd4f3af8 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -69,8 +69,8 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix())) hello.SessionId[0] = 1 - hello.SessionId[1] = 7 - hello.SessionId[2] = 5 + hello.SessionId[1] = 8 + hello.SessionId[2] = 0 copy(hello.SessionId[8:], realityConfig.ShortID[:]) //log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16]) From 5737fbc23cad0ac84f86d0e00002a6031c55ee6b Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 23 Mar 2023 12:58:59 +0800 Subject: [PATCH 130/149] chore: `proxy-server-nameserver` does not follow the `nameserver-policy` --- dns/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/resolver.go b/dns/resolver.go index 57f581a5..c16aad40 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -516,7 +516,7 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver { main: old.proxyServer, lruCache: old.lruCache, hosts: old.hosts, - policy: old.policy, + policy: trie.New[*Policy](), ipv6Timeout: old.ipv6Timeout, } return r From fd0580bfddeb840597379034ab8bae509e7562ae Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 23 Mar 2023 14:05:16 +0800 Subject: [PATCH 131/149] fix: sing_tun apply udpTimeout when using gvisor stack --- go.mod | 4 +-- go.sum | 8 +++--- listener/sing/sing.go | 49 ++++++++++++++++++++++++++----------- listener/sing_tun/server.go | 10 +++++--- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 661adc9a..2ae77925 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( 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.2.0 + github.com/sagernet/sing v0.2.1-0.20230323055925-1c4c60c739ef github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 @@ -42,7 +42,7 @@ require ( go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 golang.org/x/crypto v0.7.0 - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.6.0 diff --git a/go.sum b/go.sum index 6f3236f2..db54998b 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ 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.2.0 h1:iyc4TaeXG5XYXixl48zSDDTw46C9NOEAVFq6ZE0dA2k= -github.com/sagernet/sing v0.2.0/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing v0.2.1-0.20230323055925-1c4c60c739ef h1:gCIUmEaAbTZnQU6DPcnJkqnD9D0W2f3mp/mx0HKiWI8= +github.com/sagernet/sing v0.2.1-0.20230323055925-1c4c60c739ef/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= @@ -173,8 +173,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 9436fcfb..d6ff81c3 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -3,6 +3,7 @@ package sing import ( "context" "errors" + "go.uber.org/atomic" "golang.org/x/exp/slices" "net" "net/netip" @@ -14,6 +15,7 @@ import ( "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" + tun "github.com/metacubex/sing-tun" vmess "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" @@ -25,10 +27,11 @@ import ( const UDPTimeout = 5 * time.Minute type ListenerHandler struct { - TcpIn chan<- C.ConnContext - UdpIn chan<- C.PacketAdapter - Type C.Type - Additions []inbound.Addition + TcpIn chan<- C.ConnContext + UdpIn chan<- C.PacketAdapter + Type C.Type + Additions []inbound.Addition + UDPTimeout time.Duration } type waitCloseConn struct { @@ -96,11 +99,23 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. defer mutex.Unlock() conn2 = nil }() + lastWrite := atomic.NewTime(time.Now()) + needTimeout := tun.NeedTimeoutFromContext(ctx) // gvisor stack call NewPacketConnection() with ContextWithNeedTimeout() + udpTimeout := h.UDPTimeout + if udpTimeout == 0 { + udpTimeout = UDPTimeout + } for { buff := buf.NewPacket() // do not use stack buffer + if needTimeout { + _ = conn.SetReadDeadline(time.Now().Add(udpTimeout)) + } dest, err := conn.ReadPacket(buff) if err != nil { buff.Release() + if needTimeout && E.IsTimeout(err) && time.Now().Sub(lastWrite.Load()) < udpTimeout { + continue // someone write successful in time, so we continue read instead of return error + } if E.IsClosed(err) { break } @@ -108,11 +123,12 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } target := socks5.ParseAddr(dest.String()) packet := &packet{ - conn: &conn2, - mutex: &mutex, - rAddr: metadata.Source.UDPAddr(), - lAddr: conn.LocalAddr(), - buff: buff, + conn: &conn2, + mutex: &mutex, + rAddr: metadata.Source.UDPAddr(), + lAddr: conn.LocalAddr(), + buff: buff, + lastWrite: lastWrite, } select { case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...): @@ -127,11 +143,12 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) { } type packet struct { - conn *network.PacketConn - mutex *sync.Mutex - rAddr net.Addr - lAddr net.Addr - buff *buf.Buffer + conn *network.PacketConn + mutex *sync.Mutex + rAddr net.Addr + lAddr net.Addr + buff *buf.Buffer + lastWrite *atomic.Time } func (c *packet) Data() []byte { @@ -159,6 +176,10 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { return } err = conn.WritePacket(buff, M.SocksaddrFromNet(addr)) + if err != nil { + return + } + c.lastWrite.Store(time.Now()) return } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index d7f42c98..ada9d1c2 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -8,6 +8,7 @@ import ( "runtime" "strconv" "strings" + "time" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/dialer" @@ -151,10 +152,11 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte handler := &ListenerHandler{ ListenerHandler: sing.ListenerHandler{ - TcpIn: tcpIn, - UdpIn: udpIn, - Type: C.TUN, - Additions: additions, + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.TUN, + Additions: additions, + UDPTimeout: time.Second * time.Duration(udpTimeout), }, DnsAdds: dnsAdds, } From 7e10d78d53e9a23f0bec4cd24afa84af767a5fab Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 23 Mar 2023 18:35:37 +0800 Subject: [PATCH 132/149] chore: share the same geodata in different rule --- component/geodata/utils.go | 42 +++++++++++++++++++++++++++----------- config/updateGeo.go | 2 ++ dns/filters.go | 25 +++-------------------- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/component/geodata/utils.go b/component/geodata/utils.go index 9e7e50b1..f1ea7151 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -2,6 +2,9 @@ package geodata import ( "fmt" + "golang.org/x/sync/singleflight" + "strings" + "github.com/Dreamacro/clash/component/geodata/router" C "github.com/Dreamacro/clash/constant" ) @@ -34,6 +37,8 @@ func Verify(name string) error { } } +var loadGeoSiteMatcherSF = singleflight.Group{} + func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) { if len(countryCode) == 0 { return nil, 0, fmt.Errorf("country code could not be empty") @@ -44,16 +49,19 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) not = true countryCode = countryCode[1:] } + countryCode = strings.ToLower(countryCode) - geoLoader, err := GetGeoDataLoader(geoLoaderName) - if err != nil { - return nil, 0, err - } - - domains, err := geoLoader.LoadGeoSite(countryCode) + v, err, _ := loadGeoSiteMatcherSF.Do(countryCode, func() (interface{}, error) { + geoLoader, err := GetGeoDataLoader(geoLoaderName) + if err != nil { + return nil, err + } + return geoLoader.LoadGeoSite(countryCode) + }) if err != nil { return nil, 0, err } + domains := v.([]*router.Domain) /** linear: linear algorithm @@ -68,25 +76,31 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) return matcher, len(domains), nil } +var loadGeoIPMatcherSF = singleflight.Group{} + func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { if len(country) == 0 { return nil, 0, fmt.Errorf("country code could not be empty") } - geoLoader, err := GetGeoDataLoader(geoLoaderName) - if err != nil { - return nil, 0, err - } not := false if country[0] == '!' { not = true country = country[1:] } + country = strings.ToLower(country) - records, err := geoLoader.LoadGeoIP(country) + v, err, _ := loadGeoIPMatcherSF.Do(country, func() (interface{}, error) { + geoLoader, err := GetGeoDataLoader(geoLoaderName) + if err != nil { + return nil, err + } + return geoLoader.LoadGeoIP(country) + }) if err != nil { return nil, 0, err } + records := v.([]*router.CIDR) geoIP := &router.GeoIP{ CountryCode: country, @@ -98,6 +112,10 @@ func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { if err != nil { return nil, 0, err } - return matcher, len(records), nil } + +func ClearCache() { + loadGeoSiteMatcherSF = singleflight.Group{} + loadGeoIPMatcherSF = singleflight.Group{} +} diff --git a/config/updateGeo.go b/config/updateGeo.go index a5f7b17b..698bd52d 100644 --- a/config/updateGeo.go +++ b/config/updateGeo.go @@ -63,6 +63,8 @@ func UpdateGeoDatabases() error { return fmt.Errorf("can't save GeoSite database file: %w", err) } + geodata.ClearCache() + return nil } diff --git a/dns/filters.go b/dns/filters.go index 11c85c2c..58b261ac 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -29,29 +29,10 @@ func (gf *geoipFilter) Match(ip netip.Addr) bool { } if geoIPMatcher == nil { - countryCode := "cn" - geoLoader, err := geodata.GetGeoDataLoader(geodata.LoaderName()) + var err error + geoIPMatcher, _, err = geodata.LoadGeoIPMatcher("CN") if err != nil { - log.Errorln("[GeoIPFilter] GetGeoDataLoader error: %s", err.Error()) - return false - } - - records, err := geoLoader.LoadGeoIP(countryCode) - if err != nil { - log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error()) - return false - } - - geoIP := &router.GeoIP{ - CountryCode: countryCode, - Cidr: records, - ReverseMatch: false, - } - - geoIPMatcher, err = router.NewGeoIPMatcher(geoIP) - - if err != nil { - log.Errorln("[GeoIPFilter] NewGeoIPMatcher error: %s", err.Error()) + log.Errorln("[GeoIPFilter] LoadGeoIPMatcher error: %s", err.Error()) return false } } From a7944f1369b8ca9a7f23d0fa605c72c16f38cc65 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 23 Mar 2023 18:58:24 +0800 Subject: [PATCH 133/149] chore: better geodata shared --- component/geodata/geodata.go | 47 ++----------------------------- component/geodata/geodataproto.go | 1 - component/geodata/utils.go | 45 +++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 49 deletions(-) diff --git a/component/geodata/geodata.go b/component/geodata/geodata.go index ac0f820e..9d0b0df0 100644 --- a/component/geodata/geodata.go +++ b/component/geodata/geodata.go @@ -1,13 +1,10 @@ package geodata import ( - "errors" "fmt" - C "github.com/Dreamacro/clash/constant" - "strings" "github.com/Dreamacro/clash/component/geodata/router" - "github.com/Dreamacro/clash/log" + C "github.com/Dreamacro/clash/constant" ) type loader struct { @@ -15,47 +12,7 @@ type loader struct { } func (l *loader) LoadGeoSite(list string) ([]*router.Domain, error) { - return l.LoadGeoSiteWithAttr(C.GeositeName, list) -} - -func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { - parts := strings.Split(siteWithAttr, "@") - if len(parts) == 0 { - return nil, errors.New("empty rule") - } - list := strings.TrimSpace(parts[0]) - attrVal := parts[1:] - - if len(list) == 0 { - return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr) - } - - domains, err := l.LoadSiteByPath(file, list) - if err != nil { - return nil, err - } - - attrs := parseAttrs(attrVal) - if attrs.IsEmpty() { - if strings.Contains(siteWithAttr, "@") { - log.Warnln("empty attribute list: %s", siteWithAttr) - } - return domains, nil - } - - filteredDomains := make([]*router.Domain, 0, len(domains)) - hasAttrMatched := false - for _, domain := range domains { - if attrs.Match(domain) { - hasAttrMatched = true - filteredDomains = append(filteredDomains, domain) - } - } - if !hasAttrMatched { - log.Warnln("attribute match no rule: geosite: %s", siteWithAttr) - } - - return filteredDomains, nil + return l.LoadSiteByPath(C.GeositeName, list) } func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) { diff --git a/component/geodata/geodataproto.go b/component/geodata/geodataproto.go index ffefc484..34bdad70 100644 --- a/component/geodata/geodataproto.go +++ b/component/geodata/geodataproto.go @@ -14,6 +14,5 @@ type LoaderImplementation interface { type Loader interface { LoaderImplementation LoadGeoSite(list string) ([]*router.Domain, error) - LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) LoadGeoIP(country string) ([]*router.CIDR, error) } diff --git a/component/geodata/utils.go b/component/geodata/utils.go index f1ea7151..04ccfa51 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -1,12 +1,14 @@ package geodata import ( + "errors" "fmt" "golang.org/x/sync/singleflight" "strings" "github.com/Dreamacro/clash/component/geodata/router" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" ) var geoLoaderName = "memconservative" @@ -51,18 +53,52 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) } countryCode = strings.ToLower(countryCode) - v, err, _ := loadGeoSiteMatcherSF.Do(countryCode, func() (interface{}, error) { + parts := strings.Split(countryCode, "@") + if len(parts) == 0 { + return nil, 0, errors.New("empty rule") + } + listName := strings.TrimSpace(parts[0]) + attrVal := parts[1:] + + if len(listName) == 0 { + return nil, 0, fmt.Errorf("empty listname in rule: %s", countryCode) + } + + v, err, shared := loadGeoSiteMatcherSF.Do(listName, func() (interface{}, error) { geoLoader, err := GetGeoDataLoader(geoLoaderName) if err != nil { return nil, err } - return geoLoader.LoadGeoSite(countryCode) + return geoLoader.LoadGeoSite(listName) }) if err != nil { + if !shared { + loadGeoSiteMatcherSF.Forget(listName) // don't store the error result + } return nil, 0, err } domains := v.([]*router.Domain) + attrs := parseAttrs(attrVal) + if attrs.IsEmpty() { + if strings.Contains(countryCode, "@") { + log.Warnln("empty attribute list: %s", countryCode) + } + } else { + filteredDomains := make([]*router.Domain, 0, len(domains)) + hasAttrMatched := false + for _, domain := range domains { + if attrs.Match(domain) { + hasAttrMatched = true + filteredDomains = append(filteredDomains, domain) + } + } + if !hasAttrMatched { + log.Warnln("attribute match no rule: geosite: %s", countryCode) + } + domains = filteredDomains + } + /** linear: linear algorithm matcher, err := router.NewDomainMatcher(domains) @@ -90,7 +126,7 @@ func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { } country = strings.ToLower(country) - v, err, _ := loadGeoIPMatcherSF.Do(country, func() (interface{}, error) { + v, err, shared := loadGeoIPMatcherSF.Do(country, func() (interface{}, error) { geoLoader, err := GetGeoDataLoader(geoLoaderName) if err != nil { return nil, err @@ -98,6 +134,9 @@ func LoadGeoIPMatcher(country string) (*router.GeoIPMatcher, int, error) { return geoLoader.LoadGeoIP(country) }) if err != nil { + if !shared { + loadGeoIPMatcherSF.Forget(country) // don't store the error result + } return nil, 0, err } records := v.([]*router.CIDR) From 291b5be986bbd73d9c7c3fc1b7910bba571cf69e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 23 Mar 2023 19:53:28 +0800 Subject: [PATCH 134/149] chore: move sing-tun's udpTimeout fix to there lib --- go.mod | 6 +++--- go.sum | 12 ++++++------ listener/sing/sing.go | 37 ++++++++++--------------------------- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index 2ae77925..1101def1 100644 --- a/go.mod +++ b/go.mod @@ -21,13 +21,13 @@ require ( github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 - github.com/metacubex/sing-tun v0.1.2 + github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb github.com/miekg/dns v1.1.52 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.2.1-0.20230323055925-1c4c60c739ef + github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286 github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 @@ -65,7 +65,7 @@ require ( github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be // indirect + github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index db54998b..22cc7c26 100644 --- a/go.sum +++ b/go.sum @@ -89,14 +89,14 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be h1:zg8lXHo8t+dCSPHQ/wCJui1V+eO9TSh9NoIjKNvUykA= -github.com/metacubex/gvisor v0.0.0-20230315105319-c03631d706be/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= +github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyBMBRPPMF/Sk3gKfPfdNCa4GQyR9FoA= +github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= 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.2 h1:rQzy+11rt2ZCpCNIsFab5lWoYDTqkdaurofHo8f97yU= -github.com/metacubex/sing-tun v0.1.2/go.mod h1:+2JxFqCjgSmeeTygZjZSsQbTQUUVXwC3mxnASTs/EhU= +github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 h1:LnKcLs0HI0HX4xH/2XerX+1BLXS1Uj6Xvzn20xFuCOk= +github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3/go.mod h1:0i22nk0tgkQz/N96hrhPib1O/C5AjxSnco7Mwi2YSF0= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= @@ -127,8 +127,8 @@ 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.2.1-0.20230323055925-1c4c60c739ef h1:gCIUmEaAbTZnQU6DPcnJkqnD9D0W2f3mp/mx0HKiWI8= -github.com/sagernet/sing v0.2.1-0.20230323055925-1c4c60c739ef/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286 h1:0Td2b5l1KgrdlOnbRWgFFWsyb0TLoq/tP6j9Lut4JN0= +github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index d6ff81c3..70462728 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -3,7 +3,6 @@ package sing import ( "context" "errors" - "go.uber.org/atomic" "golang.org/x/exp/slices" "net" "net/netip" @@ -15,7 +14,6 @@ import ( "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" - tun "github.com/metacubex/sing-tun" vmess "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" @@ -99,23 +97,11 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. defer mutex.Unlock() conn2 = nil }() - lastWrite := atomic.NewTime(time.Now()) - needTimeout := tun.NeedTimeoutFromContext(ctx) // gvisor stack call NewPacketConnection() with ContextWithNeedTimeout() - udpTimeout := h.UDPTimeout - if udpTimeout == 0 { - udpTimeout = UDPTimeout - } for { buff := buf.NewPacket() // do not use stack buffer - if needTimeout { - _ = conn.SetReadDeadline(time.Now().Add(udpTimeout)) - } dest, err := conn.ReadPacket(buff) if err != nil { buff.Release() - if needTimeout && E.IsTimeout(err) && time.Now().Sub(lastWrite.Load()) < udpTimeout { - continue // someone write successful in time, so we continue read instead of return error - } if E.IsClosed(err) { break } @@ -123,12 +109,11 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } target := socks5.ParseAddr(dest.String()) packet := &packet{ - conn: &conn2, - mutex: &mutex, - rAddr: metadata.Source.UDPAddr(), - lAddr: conn.LocalAddr(), - buff: buff, - lastWrite: lastWrite, + conn: &conn2, + mutex: &mutex, + rAddr: metadata.Source.UDPAddr(), + lAddr: conn.LocalAddr(), + buff: buff, } select { case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...): @@ -143,12 +128,11 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) { } type packet struct { - conn *network.PacketConn - mutex *sync.Mutex - rAddr net.Addr - lAddr net.Addr - buff *buf.Buffer - lastWrite *atomic.Time + conn *network.PacketConn + mutex *sync.Mutex + rAddr net.Addr + lAddr net.Addr + buff *buf.Buffer } func (c *packet) Data() []byte { @@ -179,7 +163,6 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { if err != nil { return } - c.lastWrite.Store(time.Now()) return } From 99ede63a9abb85c97d1b39962d12d74349e9320d Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:42:01 +0800 Subject: [PATCH 135/149] feat: add upgrade api example: curl -X POST -H "Authorization: Bearer 123456" http://ip:port/upgrade --- go.mod | 2 +- go.sum | 4 +- hub/route/server.go | 2 + hub/route/upgrade.go | 69 ++++++ hub/updater/limitedreader.go | 67 +++++ hub/updater/updater.go | 460 +++++++++++++++++++++++++++++++++++ 6 files changed, 601 insertions(+), 3 deletions(-) create mode 100644 hub/route/upgrade.go create mode 100644 hub/updater/limitedreader.go create mode 100644 hub/updater/updater.go diff --git a/go.mod b/go.mod index 1101def1..c8eb6e10 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/samber/lo v1.37.0 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 github.com/zhangyunhao116/fastrand v0.3.0 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 22cc7c26..f56aed7a 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww= github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= diff --git a/hub/route/server.go b/hub/route/server.go index 054b1ad1..848face9 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -87,6 +87,8 @@ func Start(addr string, tlsAddr string, secret string, r.Mount("/cache", cacheRouter()) r.Mount("/dns", dnsRouter()) r.Mount("/restart", restartRouter()) + r.Mount("/upgrade", upgradeRouter()) + }) if uiPath != "" { diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go new file mode 100644 index 00000000..0d772c85 --- /dev/null +++ b/hub/route/upgrade.go @@ -0,0 +1,69 @@ +package route + +import ( + "fmt" + "net/http" + "os" + "os/exec" + "runtime" + "syscall" + + "github.com/Dreamacro/clash/hub/updater" + "github.com/Dreamacro/clash/log" + "github.com/go-chi/render" + + "github.com/go-chi/chi/v5" +) + +func upgradeRouter() http.Handler { + r := chi.NewRouter() + r.Post("/", upgrade) + return r +} + +func upgrade(w http.ResponseWriter, r *http.Request) { + // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 + log.Infoln("start update") + err := updater.Update() + if err != nil { + log.Errorln("err:%s", err) + } + + execPath, err := os.Executable() + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) + return + } + + render.JSON(w, r, render.M{"status": "ok"}) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + + // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 + // The background context is used because the underlying functions wrap it + // with timeout and shut down the server, which handles current request. It + // also should be done in a separate goroutine for the same reason. + go func() { + if runtime.GOOS == "windows" { + cmd := exec.Command(execPath, os.Args[1:]...) + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Start() + if err != nil { + log.Fatalln("restarting: %s", err) + } + + os.Exit(0) + } + + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + err = syscall.Exec(execPath, os.Args, os.Environ()) + if err != nil { + log.Fatalln("restarting: %s", err) + } + }() +} diff --git a/hub/updater/limitedreader.go b/hub/updater/limitedreader.go new file mode 100644 index 00000000..c31db601 --- /dev/null +++ b/hub/updater/limitedreader.go @@ -0,0 +1,67 @@ +package updater + +import ( + "fmt" + "io" + + "golang.org/x/exp/constraints" +) + +// LimitReachedError records the limit and the operation that caused it. +type LimitReachedError struct { + Limit int64 +} + +// Error implements the [error] interface for *LimitReachedError. +// +// TODO(a.garipov): Think about error string format. +func (lre *LimitReachedError) Error() string { + return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit) +} + +// limitedReader is a wrapper for [io.Reader] limiting the input and dealing +// with errors package. +type limitedReader struct { + r io.Reader + limit int64 + n int64 +} + +// Read implements the [io.Reader] interface. +func (lr *limitedReader) Read(p []byte) (n int, err error) { + if lr.n == 0 { + return 0, &LimitReachedError{ + Limit: lr.limit, + } + } + + p = p[:Min(lr.n, int64(len(p)))] + + n, err = lr.r.Read(p) + lr.n -= int64(n) + + return n, err +} + +// LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after +// n bytes read. +func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { + if n < 0 { + return nil, &updateError{Message: "limit must be non-negative"} + } + + return &limitedReader{ + r: r, + limit: n, + n: n, + }, nil +} + +// Min returns the smaller of x or y. +func Min[T constraints.Integer | ~string](x, y T) (res T) { + if x < y { + return x + } + + return y +} diff --git a/hub/updater/updater.go b/hub/updater/updater.go new file mode 100644 index 00000000..e4b55421 --- /dev/null +++ b/hub/updater/updater.go @@ -0,0 +1,460 @@ +package updater + +import ( + "archive/zip" + "compress/gzip" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + + "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" +) + +// Updater is the AdGuard Home updater. +var ( + client http.Client + + goarch string + goos string + goarm string + gomips string + + workDir string + versionCheckURL string + + // mu protects all fields below. + mu sync.RWMutex + + // TODO(a.garipov): See if all of these fields actually have to be in + // this struct. + currentExeName string // 当前可执行文件 + updateDir string // 更新目录 + packageName string // 更新压缩文件 + backupDir string // 备份目录 + backupExeName string // 备份文件名 + updateExeName string // 更新后的可执行文件 + unpackedFile string + + baseURL = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta" + versionURL = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt" + packageURL string + latestVersion string +) + +type updateError struct { + Message string +} + +func (e *updateError) Error() string { + return fmt.Sprintf("error: %s", e.Message) +} + +// Update performs the auto-updater. It returns an error if the updater failed. +// If firstRun is true, it assumes the configuration file doesn't exist. +func Update() (err error) { + goos = runtime.GOOS + goarch = runtime.GOARCH + latestVersion = getLatestVersion() + + if latestVersion == constant.Version { + err := &updateError{Message: "Already using latest version"} + return err + } + + updateDownloadURL() + mu.Lock() + defer mu.Unlock() + + log.Infoln("current version alpha-%s", constant.Version) + + defer func() { + if err != nil { + log.Errorln("updater: failed: %v", err) + } else { + log.Infoln("updater: finished") + } + }() + + execPath, err := os.Executable() + if err != nil { + return fmt.Errorf("getting executable path: %w", err) + } + + workDir = filepath.Dir(execPath) + //log.Infoln("workDir %s", execPath) + + err = prepare(execPath) + if err != nil { + return fmt.Errorf("preparing: %w", err) + } + + defer clean() + + err = downloadPackageFile() + if err != nil { + return fmt.Errorf("downloading package file: %w", err) + } + + err = unpack() + if err != nil { + return fmt.Errorf("unpacking: %w", err) + } + + err = replace() + if err != nil { + return fmt.Errorf("replacing: %w", err) + } + + return nil +} + +// VersionCheckURL returns the version check URL. +func VersionCheckURL() (vcu string) { + mu.RLock() + defer mu.RUnlock() + + return versionCheckURL +} + +// prepare fills all necessary fields in Updater object. +func prepare(exePath string) (err error) { + updateDir = filepath.Join(workDir, "meta-update") + currentExeName = exePath + _, pkgNameOnly := filepath.Split(packageURL) + if pkgNameOnly == "" { + return fmt.Errorf("invalid PackageURL: %q", packageURL) + } + + packageName = filepath.Join(updateDir, pkgNameOnly) + //log.Infoln(packageName) + backupDir = filepath.Join(workDir, "meta-backup") + + if goos == "windows" { + updateExeName = "clash.meta" + "-" + goos + "-" + goarch + ".exe" + } else { + updateExeName = "clash.meta" + "-" + goos + "-" + goarch + } + + log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName) + + backupExeName = filepath.Join(backupDir, filepath.Base(exePath)) + updateExeName = filepath.Join(updateDir, updateExeName) + + log.Infoln( + "updater: updating using url: %s", + packageURL, + ) + + currentExeName = exePath + _, err = os.Stat(currentExeName) + if err != nil { + return fmt.Errorf("checking %q: %w", currentExeName, err) + } + + return nil +} + +// unpack extracts the files from the downloaded archive. +func unpack() error { + var err error + _, pkgNameOnly := filepath.Split(packageURL) + + log.Debugln("updater: unpacking package") + if strings.HasSuffix(pkgNameOnly, ".zip") { + unpackedFile, err = zipFileUnpack(packageName, updateDir) + if err != nil { + return fmt.Errorf(".zip unpack failed: %w", err) + } + + } else if strings.HasSuffix(pkgNameOnly, ".gz") { + unpackedFile, err = gzFileUnpack(packageName, updateDir) + if err != nil { + return fmt.Errorf(".gz unpack failed: %w", err) + } + + } else { + return fmt.Errorf("unknown package extension") + } + + return nil +} + +// replace moves the current executable with the updated one and also copies the +// supporting files. +func replace() error { + //err := copySupportingFiles(unpackedFiles, updateDir, workDir) + //if err != nil { + // return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err) + //} + + log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName) + err := os.Rename(currentExeName, backupExeName) + if err != nil { + return err + } + + if goos == "windows" { + // rename fails with "File in use" error + log.Infoln("copying:%s to %s", updateExeName, currentExeName) + err = copyFile(updateExeName, currentExeName) + } else { + err = os.Rename(updateExeName, currentExeName) + } + if err != nil { + return err + } + + return nil +} + +// clean removes the temporary directory itself and all it's contents. +func clean() { + _ = os.RemoveAll(updateDir) +} + +// MaxPackageFileSize is a maximum package file length in bytes. The largest +// package whose size is limited by this constant currently has the size of +// approximately 9 MiB. +const MaxPackageFileSize = 32 * 1024 * 1024 + +// Download package file and save it to disk +func downloadPackageFile() (err error) { + var resp *http.Response + resp, err = client.Get(packageURL) + if err != nil { + return fmt.Errorf("http request failed: %w", err) + } + + defer func() { + closeErr := resp.Body.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + var r io.Reader + r, err = LimitReader(resp.Body, MaxPackageFileSize) + if err != nil { + return fmt.Errorf("http request failed: %w", err) + } + + log.Debugln("updater: reading http body") + // This use of ReadAll is now safe, because we limited body's Reader. + body, err := io.ReadAll(r) + if err != nil { + return fmt.Errorf("io.ReadAll() failed: %w", err) + } + + log.Debugln("updateDir %s", updateDir) + err = os.Mkdir(updateDir, 0o755) + if err != nil { + fmt.Errorf("mkdir error: %w", err) + } + + log.Debugln("updater: saving package to file %s", packageName) + err = os.WriteFile(packageName, body, 0o755) + if err != nil { + return fmt.Errorf("os.WriteFile() failed: %w", err) + } + return nil +} + +// Unpack a single .gz file to the specified directory +// Existing files are overwritten +// All files are created inside outDir, subdirectories are not created +// Return the output file name +func gzFileUnpack(gzfile, outDir string) (string, error) { + f, err := os.Open(gzfile) + if err != nil { + return "", fmt.Errorf("os.Open(): %w", err) + } + + defer func() { + closeErr := f.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + gzReader, err := gzip.NewReader(f) + if err != nil { + return "", fmt.Errorf("gzip.NewReader(): %w", err) + } + + defer func() { + closeErr := gzReader.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + // Get the original file name from the .gz file header + originalName := gzReader.Header.Name + if originalName == "" { + // Fallback: remove the .gz extension from the input file name if the header doesn't provide the original name + originalName = filepath.Base(gzfile) + originalName = strings.TrimSuffix(originalName, ".gz") + } + + outputName := filepath.Join(outDir, originalName) + + // Create the output file + wc, err := os.OpenFile( + outputName, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, + 0o755, + ) + if err != nil { + return "", fmt.Errorf("os.OpenFile(%s): %w", outputName, err) + } + + defer func() { + closeErr := wc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + // Copy the contents of the gzReader to the output file + _, err = io.Copy(wc, gzReader) + if err != nil { + return "", fmt.Errorf("io.Copy(): %w", err) + } + + return outputName, nil +} + +// Unpack a single file from .zip file to the specified directory +// Existing files are overwritten +// All files are created inside 'outDir', subdirectories are not created +// Return the output file name +func zipFileUnpack(zipfile, outDir string) (string, error) { + zrc, err := zip.OpenReader(zipfile) + if err != nil { + return "", fmt.Errorf("zip.OpenReader(): %w", err) + } + + defer func() { + closeErr := zrc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + if len(zrc.File) == 0 { + return "", fmt.Errorf("no files in the zip archive") + } + + // Assuming the first file in the zip archive is the target file + zf := zrc.File[0] + var rc io.ReadCloser + rc, err = zf.Open() + if err != nil { + return "", fmt.Errorf("zip file Open(): %w", err) + } + + defer func() { + closeErr := rc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + fi := zf.FileInfo() + name := fi.Name() + outputName := filepath.Join(outDir, name) + + if fi.IsDir() { + return "", fmt.Errorf("the target file is a directory") + } + + var wc io.WriteCloser + wc, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode()) + if err != nil { + return "", fmt.Errorf("os.OpenFile(): %w", err) + } + + defer func() { + closeErr := wc.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + _, err = io.Copy(wc, rc) + if err != nil { + return "", fmt.Errorf("io.Copy(): %w", err) + } + + return outputName, nil +} + +// Copy file on disk +func copyFile(src, dst string) error { + d, e := os.ReadFile(src) + if e != nil { + return e + } + e = os.WriteFile(dst, d, 0o644) + if e != nil { + return e + } + return nil +} + +func getLatestVersion() string { + resp, err := http.Get(versionURL) + if err != nil { + return "" + } + defer func() { + closeErr := resp.Body.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "" + } + content := strings.TrimRight(string(body), "\n") + log.Infoln("latest:%s", content) + return content +} + +func updateDownloadURL() { + var middle string + + if goarch == "arm" && goarm != "" { + middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion) + } else if isMIPS(goarch) && gomips != "" { + middle = fmt.Sprintf("-%s-%s-%s-%s", goos, goarch, gomips, latestVersion) + } else { + middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion) + } + + if goos == "windows" { + middle += ".zip" + } else { + middle += ".gz" + } + packageURL = baseURL + middle + //log.Infoln(packageURL) +} + +// isMIPS returns true if arch is any MIPS architecture. +func isMIPS(arch string) (ok bool) { + switch arch { + case + "mips", + "mips64", + "mips64le", + "mipsle": + return true + default: + return false + } +} From e4364cc985edd4c7da10280cd75f0220062213c9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:04:04 +0800 Subject: [PATCH 136/149] chore: update for testing the updater --- hub/updater/updater.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hub/updater/updater.go b/hub/updater/updater.go index e4b55421..8b9b7409 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -16,7 +16,8 @@ import ( "github.com/Dreamacro/clash/log" ) -// Updater is the AdGuard Home updater. +// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go +// Updater is the Clash.Meta updater. var ( client http.Client From 4d307887384cbf88c1c12ee88bc33dfe2629dc11 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 25 Mar 2023 22:56:24 +0800 Subject: [PATCH 137/149] chore: clean up code --- component/http/http.go | 5 +++-- hub/route/upgrade.go | 2 +- hub/updater/updater.go | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/component/http/http.go b/component/http/http.go index 54a3daa9..eb8478cd 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -2,14 +2,15 @@ package http import ( "context" - "github.com/Dreamacro/clash/component/tls" - "github.com/Dreamacro/clash/listener/inner" "io" "net" "net/http" URL "net/url" "strings" "time" + + "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/listener/inner" ) const ( diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 0d772c85..a4a8e683 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -10,9 +10,9 @@ import ( "github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/log" - "github.com/go-chi/render" "github.com/go-chi/chi/v5" + "github.com/go-chi/render" ) func upgradeRouter() http.Handler { diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 8b9b7409..272b5017 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -42,8 +42,8 @@ var ( updateExeName string // 更新后的可执行文件 unpackedFile string - baseURL = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta" - versionURL = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt" + baseURL string = "https://ghproxy.com/https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta" + versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt" packageURL string latestVersion string ) From 431dcfa914071eb103ffe526db162ad367e0ae4d Mon Sep 17 00:00:00 2001 From: H1JK Date: Sun, 26 Mar 2023 11:03:32 +0800 Subject: [PATCH 138/149] fix: Converter REALITY security type --- common/convert/v.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/convert/v.go b/common/convert/v.go index 7e365170..23949aab 100644 --- a/common/convert/v.go +++ b/common/convert/v.go @@ -27,7 +27,7 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m proxy["skip-cert-verify"] = false proxy["tls"] = false tls := strings.ToLower(query.Get("security")) - if strings.HasSuffix(tls, "tls") { + if strings.HasSuffix(tls, "tls") || tls == "reality" { proxy["tls"] = true if fingerprint := query.Get("fp"); fingerprint == "" { proxy["client-fingerprint"] = "chrome" From 545cbeeec067cb4f3d201293570b9f8cf7285878 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 27 Mar 2023 00:49:32 +0800 Subject: [PATCH 139/149] chore: skip restart when update error --- hub/route/upgrade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index a4a8e683..b60d60d4 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -27,6 +27,7 @@ func upgrade(w http.ResponseWriter, r *http.Request) { err := updater.Update() if err != nil { log.Errorln("err:%s", err) + return } execPath, err := os.Executable() From 6ca14c814efb3c38ad8f15179511854bc7ffbf6e Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 27 Mar 2023 22:18:54 +0800 Subject: [PATCH 140/149] fix: tproxy listener cannot listen udp --- listener/inbound/tproxy.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index fa458d2c..00cd0849 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -56,13 +56,10 @@ func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return err } if t.udp { - if t.lUDP != nil { - t.lUDP, err = tproxy.NewUDP(t.RawAddress(), udpIn, natTable, t.Additions()...) - if err != nil { - return err - } + t.lUDP, err = tproxy.NewUDP(t.RawAddress(), udpIn, natTable, t.Additions()...) + if err != nil { + return err } - } log.Infoln("TProxy[%s] proxy listening at: %s", t.Name(), t.Address()) return nil From 6d40de2179a644a722ee314554ecac5df9f7fdda Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 27 Mar 2023 22:27:59 +0800 Subject: [PATCH 141/149] chore: adjust trust cert --- component/tls/config.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/component/tls/config.go b/component/tls/config.go index f0155d78..91b89f1d 100644 --- a/component/tls/config.go +++ b/component/tls/config.go @@ -14,7 +14,7 @@ import ( xtls "github.com/xtls/go" ) -var trustCert, _ = x509.SystemCertPool() +var trustCerts []*x509.Certificate var mutex sync.RWMutex var errNotMacth error = errors.New("certificate fingerprints do not match") @@ -25,16 +25,28 @@ func AddCertificate(certificate string) error { if certificate == "" { return fmt.Errorf("certificate is empty") } - if ok := trustCert.AppendCertsFromPEM([]byte(certificate)); !ok { + if cert, err := x509.ParseCertificate([]byte(certificate)); err == nil { + trustCerts = append(trustCerts, cert) + return nil + } else { return fmt.Errorf("add certificate failed") } - return nil } func ResetCertificate() { mutex.Lock() defer mutex.Unlock() - trustCert, _ = x509.SystemCertPool() + trustCerts = nil +} + +func getCertPool() *x509.CertPool { + certPool, err := x509.SystemCertPool() + if err == nil { + for _, cert := range trustCerts { + certPool.AddCert(cert) + } + } + return certPool } func verifyFingerprint(fingerprint *[32]byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { @@ -84,12 +96,13 @@ func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) } func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config { + certPool := getCertPool() if tlsConfig == nil { return &tls.Config{ - RootCAs: trustCert, + RootCAs: certPool, } } - tlsConfig.RootCAs = trustCert + tlsConfig.RootCAs = certPool return tlsConfig } @@ -106,12 +119,13 @@ func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint strin } func GetGlobalXTLSConfig(tlsConfig *xtls.Config) *xtls.Config { + certPool := getCertPool() if tlsConfig == nil { return &xtls.Config{ - RootCAs: trustCert, + RootCAs: certPool, } } - tlsConfig.RootCAs = trustCert + tlsConfig.RootCAs = certPool return tlsConfig } From 34c91e5fe0cf964247cf1c4fe1ebbc7281e0b2f0 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 28 Mar 2023 16:40:45 +0000 Subject: [PATCH 142/149] chore: add release branch --- .github/workflows/build.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc43a10e..8c853ab0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -268,6 +268,18 @@ jobs: generate_release_notes: true body_path: release.txt + - name: Git push assets to "release" branch + run: | + cd bin || exit 1 + git init + git config --local user.name "github-actions[bot]" + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b release + git add . + git commit -m "${{ env.BUILDTIME }}" + git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}" + git push -f -u origin release + Upload-Release: permissions: write-all if: ${{ github.ref_type=='tag' }} From 1fdd1f702e7cd2bedba2b9cbcdadd5f9c497c64c Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Tue, 28 Mar 2023 17:00:21 +0000 Subject: [PATCH 143/149] chore: better rename --- .github/rename-cgo.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/rename-cgo.sh b/.github/rename-cgo.sh index 54841712..a0d736de 100644 --- a/.github/rename-cgo.sh +++ b/.github/rename-cgo.sh @@ -15,6 +15,15 @@ do elif [[ $FILENAME =~ "windows-4.0-amd64" ]];then echo "rename windows amd64 $FILENAME" mv $FILENAME clash.meta-windows-amd64-cgo.exe + elif [[ $FILENAME =~ "clash.meta-linux-arm-5" ]];then + echo "rename clash.meta-linux-arm-5 $FILENAME" + mv $FILENAME clash.meta-linux-armv5-cgo + elif [[ $FILENAME =~ "clash.meta-linux-arm-6" ]];then + echo "rename clash.meta-linux-arm-6 $FILENAME" + mv $FILENAME clash.meta-linux-armv6-cgo + elif [[ $FILENAME =~ "clash.meta-linux-arm-7" ]];then + echo "rename clash.meta-linux-arm-7 $FILENAME" + mv $FILENAME clash.meta-linux-armv7-cgo elif [[ $FILENAME =~ "linux" ]];then echo "rename linux $FILENAME" mv $FILENAME $FILENAME-cgo From d730feecb43aa85c409bb1b33775177d83184f0c Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 29 Mar 2023 06:03:13 +0000 Subject: [PATCH 144/149] chore: use inner for upgrade core --- constant/path.go | 6 ++--- hub/updater/updater.go | 55 +++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/constant/path.go b/constant/path.go index 707c53f6..29ac9872 100644 --- a/constant/path.go +++ b/constant/path.go @@ -66,7 +66,7 @@ func (p *path) MMDB() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(strings.ToLower(fi.Name()), "country.mmdb") { + if strings.EqualFold(fi.Name(), "Country.mmdb") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } @@ -93,7 +93,7 @@ func (p *path) GeoIP() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(strings.ToLower(fi.Name()), "geoip.dat") { + if strings.EqualFold(fi.Name(), "GeoIP.dat") { GeoipName = fi.Name() return P.Join(p.homeDir, fi.Name()) } @@ -112,7 +112,7 @@ func (p *path) GeoSite() string { // 目录则直接跳过 continue } else { - if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") { + if strings.EqualFold(fi.Name(), "GeoSite.dat") { GeositeName = fi.Name() return P.Join(p.homeDir, fi.Name()) } diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 272b5017..84b0040f 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -3,6 +3,7 @@ package updater import ( "archive/zip" "compress/gzip" + "context" "fmt" "io" "net/http" @@ -11,7 +12,9 @@ import ( "runtime" "strings" "sync" + "time" + clashHttp "github.com/Dreamacro/clash/component/http" "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" ) @@ -19,21 +22,16 @@ import ( // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go // Updater is the Clash.Meta updater. var ( - client http.Client - goarch string goos string goarm string gomips string - workDir string - versionCheckURL string + workDir string // mu protects all fields below. mu sync.RWMutex - // TODO(a.garipov): See if all of these fields actually have to be in - // this struct. currentExeName string // 当前可执行文件 updateDir string // 更新目录 packageName string // 更新压缩文件 @@ -42,8 +40,8 @@ var ( updateExeName string // 更新后的可执行文件 unpackedFile string - baseURL string = "https://ghproxy.com/https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta" - versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt" + baseURL string = "https://testingcf.jsdelivr.net/gh/MetaCubeX/Clash.Meta@release/clash.meta" + versionURL string = "https://raw.githubusercontent.com/MetaCubeX/Clash.Meta/release/version.txt" packageURL string latestVersion string ) @@ -61,7 +59,13 @@ func (e *updateError) Error() string { func Update() (err error) { goos = runtime.GOOS goarch = runtime.GOARCH - latestVersion = getLatestVersion() + latestVersion, err = getLatestVersion() + if err != nil { + err := &updateError{Message: err.Error()} + return err + } + + log.Infoln("current version alpha-%s, latest version alpha-%s", constant.Version, latestVersion) if latestVersion == constant.Version { err := &updateError{Message: "Already using latest version"} @@ -72,8 +76,6 @@ func Update() (err error) { mu.Lock() defer mu.Unlock() - log.Infoln("current version alpha-%s", constant.Version) - defer func() { if err != nil { log.Errorln("updater: failed: %v", err) @@ -115,14 +117,6 @@ func Update() (err error) { return nil } -// VersionCheckURL returns the version check URL. -func VersionCheckURL() (vcu string) { - mu.RLock() - defer mu.RUnlock() - - return versionCheckURL -} - // prepare fills all necessary fields in Updater object. func prepare(exePath string) (err error) { updateDir = filepath.Join(workDir, "meta-update") @@ -226,8 +220,11 @@ const MaxPackageFileSize = 32 * 1024 * 1024 // Download package file and save it to disk func downloadPackageFile() (err error) { - var resp *http.Response - resp, err = client.Get(packageURL) + // var resp *http.Response + // resp, err = client.Get(packageURL) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return fmt.Errorf("http request failed: %w", err) } @@ -255,11 +252,11 @@ func downloadPackageFile() (err error) { log.Debugln("updateDir %s", updateDir) err = os.Mkdir(updateDir, 0o755) if err != nil { - fmt.Errorf("mkdir error: %w", err) + return fmt.Errorf("mkdir error: %w", err) } log.Debugln("updater: saving package to file %s", packageName) - err = os.WriteFile(packageName, body, 0o755) + err = os.WriteFile(packageName, body, 0o644) if err != nil { return fmt.Errorf("os.WriteFile() failed: %w", err) } @@ -405,10 +402,12 @@ func copyFile(src, dst string) error { return nil } -func getLatestVersion() string { - resp, err := http.Get(versionURL) +func getLatestVersion() (version string, err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { - return "" + return "", fmt.Errorf("get Latest Version fail: %w", err) } defer func() { closeErr := resp.Body.Close() @@ -419,11 +418,11 @@ func getLatestVersion() string { body, err := io.ReadAll(resp.Body) if err != nil { - return "" + return "", fmt.Errorf("get Latest Version fail: %w", err) } content := strings.TrimRight(string(body), "\n") log.Infoln("latest:%s", content) - return content + return content, nil } func updateDownloadURL() { From 2c7153cd7a4fbd4fc1deb07cac06c8dffbf127ca Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 29 Mar 2023 16:19:26 +0800 Subject: [PATCH 145/149] chore: clean up code --- hub/route/restart.go | 2 +- hub/route/upgrade.go | 44 +------------------------------------------- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index bbf83f5e..9539296e 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -47,7 +47,7 @@ func restart(w http.ResponseWriter, r *http.Request) { cmd.Stderr = os.Stderr err = cmd.Start() if err != nil { - log.Fatalln("restarting:: %s", err) + log.Fatalln("restarting: %s", err) } os.Exit(0) diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index b60d60d4..5adf79eb 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -1,18 +1,12 @@ package route import ( - "fmt" "net/http" - "os" - "os/exec" - "runtime" - "syscall" "github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/log" "github.com/go-chi/chi/v5" - "github.com/go-chi/render" ) func upgradeRouter() http.Handler { @@ -30,41 +24,5 @@ func upgrade(w http.ResponseWriter, r *http.Request) { return } - execPath, err := os.Executable() - if err != nil { - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) - return - } - - render.JSON(w, r, render.M{"status": "ok"}) - if f, ok := w.(http.Flusher); ok { - f.Flush() - } - - // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180 - // The background context is used because the underlying functions wrap it - // with timeout and shut down the server, which handles current request. It - // also should be done in a separate goroutine for the same reason. - go func() { - if runtime.GOOS == "windows" { - cmd := exec.Command(execPath, os.Args[1:]...) - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - log.Fatalln("restarting: %s", err) - } - - os.Exit(0) - } - - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - err = syscall.Exec(execPath, os.Args, os.Environ()) - if err != nil { - log.Fatalln("restarting: %s", err) - } - }() + restart(w, r) } From 7c80c88feb0aea7b83f31bed284dc3cf69056088 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:26:10 +0000 Subject: [PATCH 146/149] chore: push latest alpha core to `MetaCubeX/AlphaBinary` --- .github/workflows/build.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c853ab0..b90c06ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -268,17 +268,13 @@ jobs: generate_release_notes: true body_path: release.txt - - name: Git push assets to "release" branch - run: | - cd bin || exit 1 - git init - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git checkout -b release - git add . - git commit -m "${{ env.BUILDTIME }}" - git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}" - git push -f -u origin release + - name: Push to `MetaCubeX/AlphaBinary` + uses: s0/git-publish-subdir-action@develop + env: + REPO: git@github.com:MetaCubeX/AlphaBinary.git + BRANCH: release + FOLDER: bin + SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_PRIVATE_KEY }} Upload-Release: permissions: write-all From db7623968d1c677974335473b5eae255bc0c26ee Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 29 Mar 2023 20:50:46 +0800 Subject: [PATCH 147/149] fix: inner http use host of address --- adapter/inbound/socket.go | 2 ++ component/http/http.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 4024ee42..a6b1288c 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -42,6 +42,8 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { if host == "" { if ip, err := netip.ParseAddr(h); err == nil { metadata.DstIP = ip + } else { + metadata.Host = h } } } diff --git a/component/http/http.go b/component/http/http.go index eb8478cd..ece7b442 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -53,7 +53,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { - conn := inner.HandleTcp(address, urlRes.Hostname()) + conn := inner.HandleTcp(address, "") return conn, nil }, TLSClientConfig: tls.GetDefaultTLSConfig(), From 2fef329319a33630c0d66703012ff8963e758476 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:59:36 +0000 Subject: [PATCH 148/149] fix: upgrade backup --- .github/workflows/build.yml | 8 ------ hub/updater/updater.go | 55 +++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b90c06ef..dc43a10e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -268,14 +268,6 @@ jobs: generate_release_notes: true body_path: release.txt - - name: Push to `MetaCubeX/AlphaBinary` - uses: s0/git-publish-subdir-action@develop - env: - REPO: git@github.com:MetaCubeX/AlphaBinary.git - BRANCH: release - FOLDER: bin - SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_PRIVATE_KEY }} - Upload-Release: permissions: write-all if: ${{ github.ref_type=='tag' }} diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 84b0040f..077818fd 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -38,10 +38,9 @@ var ( backupDir string // 备份目录 backupExeName string // 备份文件名 updateExeName string // 更新后的可执行文件 - unpackedFile string - baseURL string = "https://testingcf.jsdelivr.net/gh/MetaCubeX/Clash.Meta@release/clash.meta" - versionURL string = "https://raw.githubusercontent.com/MetaCubeX/Clash.Meta/release/version.txt" + baseURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta" + versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt" packageURL string latestVersion string ) @@ -57,6 +56,9 @@ func (e *updateError) Error() string { // Update performs the auto-updater. It returns an error if the updater failed. // If firstRun is true, it assumes the configuration file doesn't exist. func Update() (err error) { + mu.Lock() + defer mu.Unlock() + goos = runtime.GOOS goarch = runtime.GOARCH latestVersion, err = getLatestVersion() @@ -73,8 +75,6 @@ func Update() (err error) { } updateDownloadURL() - mu.Lock() - defer mu.Unlock() defer func() { if err != nil { @@ -90,7 +90,6 @@ func Update() (err error) { } workDir = filepath.Dir(execPath) - //log.Infoln("workDir %s", execPath) err = prepare(execPath) if err != nil { @@ -109,6 +108,11 @@ func Update() (err error) { return fmt.Errorf("unpacking: %w", err) } + err = backup() + if err != nil { + return fmt.Errorf("replacing: %w", err) + } + err = replace() if err != nil { return fmt.Errorf("replacing: %w", err) @@ -136,7 +140,7 @@ func prepare(exePath string) (err error) { updateExeName = "clash.meta" + "-" + goos + "-" + goarch } - log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName) + log.Infoln("updateExeName: %s ", updateExeName) backupExeName = filepath.Join(backupDir, filepath.Base(exePath)) updateExeName = filepath.Join(updateDir, updateExeName) @@ -162,13 +166,13 @@ func unpack() error { log.Debugln("updater: unpacking package") if strings.HasSuffix(pkgNameOnly, ".zip") { - unpackedFile, err = zipFileUnpack(packageName, updateDir) + _, err = zipFileUnpack(packageName, updateDir) if err != nil { return fmt.Errorf(".zip unpack failed: %w", err) } } else if strings.HasSuffix(pkgNameOnly, ".gz") { - unpackedFile, err = gzFileUnpack(packageName, updateDir) + _, err = gzFileUnpack(packageName, updateDir) if err != nil { return fmt.Errorf(".gz unpack failed: %w", err) } @@ -180,25 +184,37 @@ func unpack() error { return nil } +// backup makes a backup of the current configuration and supporting files. It +// ignores the configuration file if firstRun is true. +func backup() (err error) { + log.Infoln("updater: backing up current Exefile") + _ = os.Mkdir(backupDir, 0o755) + + err = copyFile(currentExeName, backupExeName) + if err != nil { + return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", currentExeName, backupExeName, err) + } + + return nil +} + // replace moves the current executable with the updated one and also copies the // supporting files. func replace() error { - //err := copySupportingFiles(unpackedFiles, updateDir, workDir) - //if err != nil { - // return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err) - //} + var err error - log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName) - err := os.Rename(currentExeName, backupExeName) - if err != nil { - return err - } + // log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName) + // err := os.Rename(currentExeName, backupExeName) + // if err != nil { + // return err + // } if goos == "windows" { // rename fails with "File in use" error - log.Infoln("copying:%s to %s", updateExeName, currentExeName) + log.Infoln("copying: %s to %s", updateExeName, currentExeName) err = copyFile(updateExeName, currentExeName) } else { + log.Infoln("copying: %s to %s", updateExeName, currentExeName) err = os.Rename(updateExeName, currentExeName) } if err != nil { @@ -421,7 +437,6 @@ func getLatestVersion() (version string, err error) { return "", fmt.Errorf("get Latest Version fail: %w", err) } content := strings.TrimRight(string(body), "\n") - log.Infoln("latest:%s", content) return content, nil } From 991de009beadf0b7cb82600cbbb578ea55c6b0d8 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:57:52 +0000 Subject: [PATCH 149/149] chore: update readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e485db2..9339a935 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,7 @@ - Comprehensive HTTP RESTful API controller ## Wiki - -Documentation and configuring examples are available on [Clash.Meta Wiki](https://clash-meta.wiki). +Configuration examples can be found at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be found [Clash.Meta Wiki](https://clash-meta.wiki). ## Build