From 00695137802fef8e43bf17381097d1b68b57b85d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 11 Jan 2023 10:19:30 +0800 Subject: [PATCH] chore: shadowtls don't depend on trojan's code --- adapter/outbound/shadowsocks.go | 33 ++++++++++++++----- transport/shadowtls/shadowtls.go | 56 +++++++++++++++++++------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 736bb5bd..8df84c7c 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -2,6 +2,7 @@ package outbound import ( "context" + "crypto/tls" "errors" "fmt" "net" @@ -9,11 +10,11 @@ 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" "github.com/Dreamacro/clash/transport/socks5" - "github.com/Dreamacro/clash/transport/trojan" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" shadowsocks "github.com/metacubex/sing-shadowsocks" @@ -33,7 +34,7 @@ type ShadowSocks struct { obfsOption *simpleObfsOption v2rayOption *v2rayObfs.Option shadowTLSOption *shadowTLSOption - tlsConnector *trojan.Trojan + tlsConfig *tls.Config } type ShadowSocksOption struct { @@ -66,8 +67,10 @@ type v2rayObfsOption struct { } type shadowTLSOption struct { - Password string `obfs:"password"` - Host string `obfs:"host"` + Password string `obfs:"password"` + Host string `obfs:"host"` + Fingerprint string `obfs:"fingerprint,omitempty"` + SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` } // StreamConn implements C.ProxyAdapter @@ -85,10 +88,7 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } case shadowtls.Mode: - if ss.tlsConnector == nil { - ss.tlsConnector = trojan.New(&trojan.Option{ServerName: ss.shadowTLSOption.Host, SkipCertVerify: false}) - } - c = shadowtls.NewShadowTls(c, ss.shadowTLSOption.Password, ss.tlsConnector) + 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")) @@ -172,6 +172,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { var v2rayOption *v2rayObfs.Option var obfsOption *simpleObfsOption var shadowTLSOpt *shadowTLSOption + var tlsConfig *tls.Config obfsMode := "" decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) @@ -213,6 +214,21 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { if err := decoder.Decode(option.PluginOpts, shadowTLSOpt); 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.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, shadowTLSOpt.Fingerprint); err != nil { + return nil, err + } + } } return &ShadowSocks{ @@ -232,6 +248,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { v2rayOption: v2rayOption, obfsOption: obfsOption, shadowTLSOption: shadowTLSOpt, + tlsConfig: tlsConfig, }, nil } diff --git a/transport/shadowtls/shadowtls.go b/transport/shadowtls/shadowtls.go index 4a0db6a9..86a23779 100644 --- a/transport/shadowtls/shadowtls.go +++ b/transport/shadowtls/shadowtls.go @@ -1,8 +1,10 @@ package shadowtls import ( + "context" "crypto/hmac" "crypto/sha1" + "crypto/tls" "encoding/binary" "fmt" "hash" @@ -10,7 +12,7 @@ import ( "net" "github.com/Dreamacro/clash/common/pool" - "github.com/Dreamacro/clash/transport/trojan" + C "github.com/Dreamacro/clash/constant" ) const ( @@ -20,13 +22,17 @@ const ( tlsHeaderLen int = 5 ) -// TLSObfs is shadowsocks tls simple-obfs implementation -type ShadowTls struct { +var ( + DefaultALPN = []string{"h2", "http/1.1"} +) + +// ShadowTLS is shadow-tls implementation +type ShadowTLS struct { net.Conn password []byte remain int firstRequest bool - tlsConnector *trojan.Trojan + tlsConfig *tls.Config } type HashedConn struct { @@ -47,9 +53,9 @@ func (h HashedConn) Read(b []byte) (n int, err error) { return } -func (to *ShadowTls) read(b []byte) (int, error) { +func (s *ShadowTLS) read(b []byte) (int, error) { buf := pool.Get(tlsHeaderLen) - _, err := io.ReadFull(to.Conn, buf) + _, err := io.ReadFull(s.Conn, buf) if err != nil { return 0, fmt.Errorf("shadowtls read failed %w", err) } @@ -60,36 +66,36 @@ func (to *ShadowTls) read(b []byte) (int, error) { pool.Put(buf) if length > len(b) { - n, err := to.Conn.Read(b) + n, err := s.Conn.Read(b) if err != nil { return n, err } - to.remain = length - n + s.remain = length - n return n, nil } - return io.ReadFull(to.Conn, b[:length]) + return io.ReadFull(s.Conn, b[:length]) } -func (to *ShadowTls) Read(b []byte) (int, error) { - if to.remain > 0 { - length := to.remain +func (s *ShadowTLS) Read(b []byte) (int, error) { + if s.remain > 0 { + length := s.remain if length > len(b) { length = len(b) } - n, err := io.ReadFull(to.Conn, b[:length]) + n, err := io.ReadFull(s.Conn, b[:length]) if err != nil { return n, fmt.Errorf("shadowtls Read failed with %w", err) } - to.remain -= n + s.remain -= n return n, nil } - return to.read(b) + return s.read(b) } -func (to *ShadowTls) Write(b []byte) (int, error) { +func (s *ShadowTLS) Write(b []byte) (int, error) { length := len(b) for i := 0; i < length; i += chunkSize { end := i + chunkSize @@ -97,7 +103,7 @@ func (to *ShadowTls) Write(b []byte) (int, error) { end = length } - n, err := to.write(b[i:end]) + n, err := s.write(b[i:end]) if err != nil { return n, fmt.Errorf("shadowtls Write failed with %w, i=%d, end=%d, n=%d", err, i, end, n) } @@ -105,11 +111,15 @@ func (to *ShadowTls) Write(b []byte) (int, error) { return length, nil } -func (s *ShadowTls) write(b []byte) (int, error) { +func (s *ShadowTLS) write(b []byte) (int, error) { var hashVal []byte if s.firstRequest { hashedConn := newHashedStream(s.Conn, s.password) - if _, err := s.tlsConnector.StreamConn(hashedConn); err != nil { + tlsConn := tls.Client(hashedConn, s.tlsConfig) + // fix tls handshake not timeout + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + if err := tlsConn.HandshakeContext(ctx); err != nil { return 0, fmt.Errorf("tls connect failed with %w", err) } hashVal = hashedConn.hasher.Sum(nil)[:hashLen] @@ -131,12 +141,12 @@ func (s *ShadowTls) write(b []byte) (int, error) { return len(b), nil } -// NewShadowTls return a ShadowTls -func NewShadowTls(conn net.Conn, password string, tlsConnector *trojan.Trojan) net.Conn { - return &ShadowTls{ +// NewShadowTLS return a ShadowTLS +func NewShadowTLS(conn net.Conn, password string, tlsConfig *tls.Config) net.Conn { + return &ShadowTLS{ Conn: conn, password: []byte(password), firstRequest: true, - tlsConnector: tlsConnector, + tlsConfig: tlsConfig, } }