github.com/kelleygo/clashcore@v1.0.2/transport/sing-shadowtls/shadowtls.go (about)

     1  package sing_shadowtls
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"net"
     7  
     8  	"github.com/kelleygo/clashcore/component/ca"
     9  	tlsC "github.com/kelleygo/clashcore/component/tls"
    10  	"github.com/kelleygo/clashcore/log"
    11  
    12  	"github.com/sagernet/sing-shadowtls"
    13  	sing_common "github.com/sagernet/sing/common"
    14  	utls "github.com/sagernet/utls"
    15  )
    16  
    17  const (
    18  	Mode string = "shadow-tls"
    19  )
    20  
    21  var (
    22  	DefaultALPN = []string{"h2", "http/1.1"}
    23  )
    24  
    25  type ShadowTLSOption struct {
    26  	Password          string
    27  	Host              string
    28  	Fingerprint       string
    29  	ClientFingerprint string
    30  	SkipCertVerify    bool
    31  	Version           int
    32  }
    33  
    34  func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
    35  	tlsConfig := &tls.Config{
    36  		NextProtos:         DefaultALPN,
    37  		MinVersion:         tls.VersionTLS12,
    38  		InsecureSkipVerify: option.SkipCertVerify,
    39  		ServerName:         option.Host,
    40  	}
    41  
    42  	var err error
    43  	tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	tlsHandshake := shadowtls.DefaultTLSHandshakeFunc(option.Password, tlsConfig)
    49  	if len(option.ClientFingerprint) != 0 {
    50  		if fingerprint, exists := tlsC.GetFingerprint(option.ClientFingerprint); exists {
    51  			tlsHandshake = uTLSHandshakeFunc(tlsConfig, *fingerprint.ClientHelloID)
    52  		}
    53  	}
    54  	client, err := shadowtls.NewClient(shadowtls.ClientConfig{
    55  		Version:      option.Version,
    56  		Password:     option.Password,
    57  		TLSHandshake: tlsHandshake,
    58  		Logger:       log.SingLogger,
    59  	})
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	return client.DialContextConn(ctx, conn)
    64  }
    65  
    66  func uTLSHandshakeFunc(config *tls.Config, clientHelloID utls.ClientHelloID) shadowtls.TLSHandshakeFunc {
    67  	return func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
    68  		tlsConfig := &utls.Config{
    69  			Rand:                  config.Rand,
    70  			Time:                  config.Time,
    71  			VerifyPeerCertificate: config.VerifyPeerCertificate,
    72  			RootCAs:               config.RootCAs,
    73  			NextProtos:            config.NextProtos,
    74  			ServerName:            config.ServerName,
    75  			InsecureSkipVerify:    config.InsecureSkipVerify,
    76  			CipherSuites:          config.CipherSuites,
    77  			MinVersion:            config.MinVersion,
    78  			MaxVersion:            config.MaxVersion,
    79  			CurvePreferences: sing_common.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {
    80  				return utls.CurveID(it)
    81  			}),
    82  			SessionTicketsDisabled: config.SessionTicketsDisabled,
    83  			Renegotiation:          utls.RenegotiationSupport(config.Renegotiation),
    84  			SessionIDGenerator:     sessionIDGenerator,
    85  		}
    86  		tlsConn := utls.UClient(conn, tlsConfig, clientHelloID)
    87  		return tlsConn.HandshakeContext(ctx)
    88  	}
    89  }