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